aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorktsj <ktsj@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-04-17 06:48:03 +0000
committerktsj <ktsj@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-04-17 06:48:03 +0000
commit9738f96fcfe50b2a605e350bdd40bd7a85665f54 (patch)
treea8495fa0a315ef4015f01db4d158b74987d18277 /test
parentb077654a2c89485c086e77c337d30a11ff3781c3 (diff)
downloadruby-9738f96fcfe50b2a605e350bdd40bd7a85665f54.tar.gz
Introduce pattern matching [EXPERIMENTAL]
[ruby-core:87945] [Feature #14912] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67586 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'test')
-rw-r--r--test/coverage/test_coverage.rb32
-rw-r--r--test/ripper/test_parser_events.rb18
-rw-r--r--test/ripper/test_sexp.rb302
-rw-r--r--test/ruby/test_pattern_matching.rb1075
4 files changed, 1427 insertions, 0 deletions
diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb
index 30523c341a..f88e97e9d6 100644
--- a/test/coverage/test_coverage.rb
+++ b/test/coverage/test_coverage.rb
@@ -359,6 +359,38 @@ class TestCoverage < Test::Unit::TestCase
end;
end
+ def test_branch_coverage_for_pattern_matching
+ result = {
+ :branches=> {
+ [:case, 0, 3, 4, 8, 7] => {[:in, 1, 5, 6, 5, 7]=>2, [:in, 2, 7, 6, 7, 7]=>0, [:else, 3, 3, 4, 8, 7]=>1},
+ [:case, 4, 12, 2, 17, 5] => {[:in, 5, 14, 4, 14, 5]=>2, [:else, 6, 16, 4, 16, 5]=>1}},
+ }
+ assert_coverage(<<~"end;", { branches: true }, result)
+ def foo(x)
+ begin
+ case x
+ in 0
+ 0
+ in 1
+ 1
+ end
+ rescue NoMatchingPatternError
+ end
+
+ case x
+ in 0
+ 0
+ else
+ 1
+ end
+ end
+
+ foo(0)
+ foo(0)
+ foo(2)
+ end;
+ end
+
def test_branch_coverage_for_safe_method_invocation
result = {
:branches=>{
diff --git a/test/ripper/test_parser_events.rb b/test/ripper/test_parser_events.rb
index be46ad917d..1d2c501c3b 100644
--- a/test/ripper/test_parser_events.rb
+++ b/test/ripper/test_parser_events.rb
@@ -1509,4 +1509,22 @@ class TestRipper::ParserEvents < Test::Unit::TestCase
assert_warn("") {fmt, = warn("\r;")}
assert_match(/encountered/, fmt)
end
+
+ def test_in
+ thru_in = false
+ parse('case 0; in 0; end', :on_in) {thru_in = true}
+ assert_equal true, thru_in
+ end
+
+ def test_aryptn
+ thru_aryptn = false
+ parse('case 0; in [0]; end', :on_aryptn) {thru_aryptn = true}
+ assert_equal true, thru_aryptn
+ end
+
+ def test_hshptn
+ thru_hshptn = false
+ parse('case 0; in {a:}; end', :on_hshptn) {thru_hshptn = true}
+ assert_equal true, thru_hshptn
+ end
end if ripper_test
diff --git a/test/ripper/test_sexp.rb b/test/ripper/test_sexp.rb
index d63464d5a7..93e40f3bd7 100644
--- a/test/ripper/test_sexp.rb
+++ b/test/ripper/test_sexp.rb
@@ -140,4 +140,306 @@ eot
s,
bug15670)
end
+
+ pattern_matching_data = {
+ %q{ case 0; in 0; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in, [:@int, "0", [1, 11]], [[:void_stmt]], nil]],
+
+ %q{ case 0; in 0 if a; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:if_mod, [:vcall, [:@ident, "a", [1, 16]]], [:@int, "0", [1, 11]]],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in 0 unless a; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:unless_mod, [:vcall, [:@ident, "a", [1, 20]]], [:@int, "0", [1, 11]]],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in a; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in, [:var_field, [:@ident, "a", [1, 11]]], [[:void_stmt]], nil]],
+
+ %q{ case 0; in a,; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn,
+ nil,
+ [[:var_field, [:@ident, "a", [1, 11]]]],
+ [:var_field, nil],
+ nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in a,b; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn,
+ nil,
+ [[:var_field, [:@ident, "a", [1, 11]]],
+ [:var_field, [:@ident, "b", [1, 13]]]],
+ nil,
+ nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in *a; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn, nil, nil, [:var_field, [:@ident, "a", [1, 12]]], nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in *a,b; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn,
+ nil,
+ nil,
+ [:var_field, [:@ident, "a", [1, 12]]],
+ [[:var_field, [:@ident, "b", [1, 14]]]]],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in *a,b,c; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn,
+ nil,
+ nil,
+ [:var_field, [:@ident, "a", [1, 12]]],
+ [[:var_field, [:@ident, "b", [1, 14]]],
+ [:var_field, [:@ident, "c", [1, 16]]]]],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in *; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in, [:aryptn, nil, nil, [:var_field, nil], nil], [[:void_stmt]], nil]],
+
+ %q{ case 0; in *,a; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn,
+ nil,
+ nil,
+ [:var_field, nil],
+ [[:var_field, [:@ident, "a", [1, 13]]]]],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in a:,**b; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn,
+ nil,
+ [[[:@label, "a:", [1, 11]], nil]],
+ [:var_field, [:@ident, "b", [1, 16]]]],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in **a; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn, nil, [], [:var_field, [:@ident, "a", [1, 13]]]],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in **; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in, [:hshptn, nil, [], nil], [[:void_stmt]], nil]],
+
+ %q{ case 0; in a: 0; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn, nil, [[[:@label, "a:", [1, 11]], [:@int, "0", [1, 14]]]], nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in a:; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn, nil, [[[:@label, "a:", [1, 11]], nil]], nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in "a": 0; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn,
+ nil,
+ [[[:string_content, [:@tstring_content, "a", [1, 12]]],
+ [:@int, "0", [1, 16]]]],
+ nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in "a":; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn,
+ nil,
+ [[[:string_content, [:@tstring_content, "a", [1, 12]]], nil]],
+ nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in a: 0, b: 0; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn,
+ nil,
+ [[[:@label, "a:", [1, 11]], [:@int, "0", [1, 14]]],
+ [[:@label, "b:", [1, 17]], [:@int, "0", [1, 20]]]],
+ nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in 0 => a; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:binary,
+ [:@int, "0", [1, 11]],
+ :"=>",
+ [:var_field, [:@ident, "a", [1, 16]]]],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in 0 | 1; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:binary, [:@int, "0", [1, 11]], :|, [:@int, "1", [1, 15]]],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in A(0); end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn,
+ [:var_ref, [:@const, "A", [1, 11]]],
+ [[:@int, "0", [1, 13]]],
+ nil,
+ nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in A(a:); end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn,
+ [:var_ref, [:@const, "A", [1, 11]]],
+ [[[:@label, "a:", [1, 13]], nil]],
+ nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in A(); end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn, [:var_ref, [:@const, "A", [1, 11]]], nil, nil, nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in A[a]; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn,
+ [:var_ref, [:@const, "A", [1, 11]]],
+ [[:var_field, [:@ident, "a", [1, 13]]]],
+ nil,
+ nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in A[a:]; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn,
+ [:var_ref, [:@const, "A", [1, 11]]],
+ [[[:@label, "a:", [1, 13]], nil]],
+ nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in A[]; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn, [:var_ref, [:@const, "A", [1, 11]]], nil, nil, nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in [a]; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:aryptn, nil, [[:var_field, [:@ident, "a", [1, 12]]]], nil, nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in []; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in, [:aryptn, nil, nil, nil, nil], [[:void_stmt]], nil]],
+
+ %q{ case 0; in {a: 0}; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in,
+ [:hshptn, nil, [[[:@label, "a:", [1, 12]], [:@int, "0", [1, 15]]]], nil],
+ [[:void_stmt]],
+ nil]],
+
+ %q{ case 0; in {}; end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in, [:hshptn, nil, nil, nil], [[:void_stmt]], nil]],
+
+ %q{ case 0; in (0); end } =>
+ [:case,
+ [:@int, "0", [1, 5]],
+ [:in, [:@int, "0", [1, 12]], [[:void_stmt]], nil]],
+
+ %q{ case 0; in a:, a:; end } =>
+ nil,
+
+ %q{ case 0; in a?:; end } =>
+ nil,
+ }
+ pattern_matching_data.each_with_index do |(src, expected), i|
+ define_method(:"test_pattern_matching_#{i}") do
+ sexp = Ripper.sexp(src.strip)
+ assert_equal expected, sexp && sexp[1][0], src
+ end
+ end
end if ripper_test
diff --git a/test/ruby/test_pattern_matching.rb b/test/ruby/test_pattern_matching.rb
new file mode 100644
index 0000000000..b0d8e4778b
--- /dev/null
+++ b/test/ruby/test_pattern_matching.rb
@@ -0,0 +1,1075 @@
+# frozen_string_literal: true
+require 'test/unit'
+
+class TestPatternMatching < Test::Unit::TestCase
+ class C
+ class << self
+ attr_accessor :keys
+ end
+
+ def initialize(obj)
+ @obj = obj
+ end
+
+ def deconstruct
+ @obj
+ end
+
+ def deconstruct_keys(keys)
+ C.keys = keys
+ @obj
+ end
+ end
+
+ def test_basic
+ assert_block do
+ case 0
+ in 0
+ true
+ else
+ false
+ end
+ end
+
+ assert_block do
+ case 0
+ in 1
+ false
+ else
+ true
+ end
+ end
+
+ assert_raise(NoMatchingPatternError) do
+ case 0
+ in 1
+ false
+ end
+ end
+
+ begin
+ o = [0]
+ case o
+ in 1
+ false
+ end
+ rescue => e
+ assert_match o.inspect, e.message
+ end
+
+ assert_block do
+ begin
+ true
+ ensure
+ case 0
+ in 0
+ false
+ end
+ end
+ end
+
+ assert_block do
+ begin
+ true
+ ensure
+ case 0
+ in 1
+ else
+ false
+ end
+ end
+ end
+
+ assert_raise(NoMatchingPatternError) do
+ begin
+ ensure
+ case 0
+ in 1
+ end
+ end
+ end
+
+ assert_block do
+ eval(%q{
+ case true
+ in a
+ a
+ end
+ })
+ end
+
+ assert_block do
+ tap do |a|
+ tap do
+ case true
+ in a
+ a
+ end
+ end
+ end
+ end
+
+ assert_raise(NoMatchingPatternError) do
+ o = BasicObject.new
+ def o.match
+ case 0
+ in 1
+ end
+ end
+ o.match
+ end
+ end
+
+ def test_modifier
+ assert_block do
+ case 0
+ in a if a == 0
+ true
+ end
+ end
+
+ assert_block do
+ case 0
+ in a if a != 0
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case 0
+ in a unless a != 0
+ true
+ end
+ end
+
+ assert_block do
+ case 0
+ in a unless a == 0
+ else
+ true
+ end
+ end
+ end
+
+ def test_as_pattern
+ assert_block do
+ case 0
+ in 0 => a
+ a == 0
+ end
+ end
+ end
+
+ def test_alternative_pattern
+ assert_block do
+ [0, 1].all? do |i|
+ case i
+ in 0 | 1
+ true
+ end
+ end
+ end
+
+ assert_block do
+ case 0
+ in _ | _a
+ true
+ end
+ end
+
+ assert_syntax_error(%q{
+ case 0
+ in a | 0
+ end
+ }, /illegal variable in alternative pattern/)
+ end
+
+ def test_var_pattern
+ assert_block do
+ case [0, 1]
+ in a, a
+ a == 1
+ end
+ end
+ end
+
+ def test_literal_value_pattern
+ assert_block do
+ case [nil, self, true, false]
+ in [nil, self, true, false]
+ true
+ end
+ end
+
+ assert_block do
+ case [0d170, 0D170, 0xaa, 0xAa, 0xAA, 0Xaa, 0XAa, 0XaA, 0252, 0o252, 0O252]
+ in [0d170, 0D170, 0xaa, 0xAa, 0xAA, 0Xaa, 0XAa, 0XaA, 0252, 0o252, 0O252]
+ true
+ end
+
+ case [0b10101010, 0B10101010, 12r, 12.3r, 1i, 12.3ri]
+ in [0b10101010, 0B10101010, 12r, 12.3r, 1i, 12.3ri]
+ true
+ end
+ end
+
+ assert_block do
+ x = 'x'
+ case ['a', 'a', x]
+ in ['a', "a", "#{x}"]
+ true
+ end
+ end
+
+ assert_block do
+ case ["a\n"]
+ in [<<END]
+a
+END
+ true
+ end
+ end
+
+ assert_block do
+ case [:a, :"a"]
+ in [:a, :"a"]
+ true
+ end
+ end
+
+ assert_block do
+ case [0, 1, 2, 3, 4, 5]
+ in [0..1, 0...2, 0.., 0..., (...5), (..5)]
+ true
+ end
+ end
+
+ assert_syntax_error(%q{
+ case 0
+ in a..b
+ end
+ }, /unexpected/)
+
+ assert_block do
+ case 'abc'
+ in /a/
+ true
+ end
+ end
+
+ assert_block do
+ case 0
+ in ->(i) { i == 0 }
+ true
+ end
+ end
+
+ assert_block do
+ case [%(a), %q(a), %Q(a), %w(a), %W(a), %i(a), %I(a), %s(a), %x(echo a), %(), %q(), %Q(), %w(), %W(), %i(), %I(), %s(), 'a']
+ in [%(a), %q(a), %Q(a), %w(a), %W(a), %i(a), %I(a), %s(a), %x(echo a), %(), %q(), %Q(), %w(), %W(), %i(), %I(), %s(), %r(a)]
+ true
+ end
+ end
+
+ assert_block do
+ case [__FILE__, __LINE__ + 1, __ENCODING__]
+ in [__FILE__, __LINE__, __ENCODING__]
+ true
+ end
+ end
+ end
+
+ def test_constant_value_pattern
+ assert_block do
+ case 0
+ in Integer
+ true
+ end
+ end
+
+ assert_block do
+ case 0
+ in Object::Integer
+ true
+ end
+ end
+
+ assert_block do
+ case 0
+ in ::Object::Integer
+ true
+ end
+ end
+ end
+
+ def test_pin_operator_value_pattern
+ assert_block do
+ a = /a/
+ case 'abc'
+ in ^a
+ true
+ end
+ end
+
+ assert_block do
+ case [0, 0]
+ in a, ^a
+ a == 0
+ end
+ end
+ end
+
+ def test_array_pattern
+ assert_block do
+ [[0], C.new([0])].all? do |i|
+ case i
+ in 0,;
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [[0, 1], C.new([0, 1])].all? do |i|
+ case i
+ in 0,;
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [[], C.new([])].all? do |i|
+ case i
+ in 0,;
+ else
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [[0, 1], C.new([0, 1])].all? do |i|
+ case i
+ in 0, 1
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [[0], C.new([0])].all? do |i|
+ case i
+ in 0, 1
+ else
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [[], C.new([])].all? do |i|
+ case i
+ in *a
+ a == []
+ end
+ end
+ end
+
+ assert_block do
+ [[0], C.new([0])].all? do |i|
+ case i
+ in *a
+ a == [0]
+ end
+ end
+ end
+
+ assert_block do
+ [[0], C.new([0])].all? do |i|
+ case i
+ in *a, 0, 1
+ else
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [[0, 1], C.new([0, 1])].all? do |i|
+ case i
+ in *a, 0, 1
+ a == []
+ end
+ end
+ end
+
+ assert_block do
+ [[0, 1, 2], C.new([0, 1, 2])].all? do |i|
+ case i
+ in *a, 1, 2
+ a == [0]
+ end
+ end
+ end
+
+ assert_block do
+ [[], C.new([])].all? do |i|
+ case i
+ in *;
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [[0], C.new([0])].all? do |i|
+ case i
+ in *, 0, 1
+ else
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [[0, 1], C.new([0, 1])].all? do |i|
+ case i
+ in *, 0, 1
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [[0, 1, 2], C.new([0, 1, 2])].all? do |i|
+ case i
+ in *, 1, 2
+ true
+ end
+ end
+ end
+
+ assert_block do
+ case C.new([0])
+ in C(0)
+ true
+ end
+ end
+
+ assert_block do
+ case C.new([0])
+ in Array(0)
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case C.new([])
+ in C()
+ true
+ end
+ end
+
+ assert_block do
+ case C.new([])
+ in Array()
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case C.new([0])
+ in C[0]
+ true
+ end
+ end
+
+ assert_block do
+ case C.new([0])
+ in Array[0]
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case C.new([])
+ in C[]
+ true
+ end
+ end
+
+ assert_block do
+ case C.new([])
+ in Array[]
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case []
+ in []
+ true
+ end
+ end
+
+ assert_block do
+ case C.new([])
+ in []
+ true
+ end
+ end
+
+ assert_block do
+ case [0]
+ in [0]
+ true
+ end
+ end
+
+ assert_block do
+ case C.new([0])
+ in [0]
+ true
+ end
+ end
+
+ assert_block do
+ case []
+ in [0, *a]
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case [0]
+ in [0, *a]
+ a == []
+ end
+ end
+
+ assert_block do
+ case [0]
+ in [0, *a, 1]
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case [0, 1]
+ in [0, *a, 1]
+ a == []
+ end
+ end
+
+ assert_block do
+ case [0, 1, 2]
+ in [0, *a, 2]
+ a == [1]
+ end
+ end
+
+ assert_block do
+ case []
+ in [0, *]
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case [0]
+ in [0, *]
+ true
+ end
+ end
+
+ assert_block do
+ case [0, 1]
+ in [0, *]
+ true
+ end
+ end
+
+ assert_block do
+ case []
+ in [0, *a]
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case [0]
+ in [0, *a]
+ a == []
+ end
+ end
+
+ assert_block do
+ case [0, 1]
+ in [0, *a]
+ a == [1]
+ end
+ end
+ end
+
+ def test_hash_pattern
+ assert_block do
+ [{}, C.new({})].all? do |i|
+ case i
+ in a: 0
+ else
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in a: 0
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
+ case i
+ in a: 0
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in a: 0, b: 1
+ else
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
+ case i
+ in a: 0, b: 1
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0, b: 1, c: 2}, C.new({a: 0, b: 1, c: 2})].all? do |i|
+ case i
+ in a: 0, b: 1
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{}, C.new({})].all? do |i|
+ case i
+ in a:
+ else
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in a:
+ a == 0
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
+ case i
+ in a:
+ a == 0
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in "a": 0
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in "a":;
+ a == 0
+ end
+ end
+ end
+
+ assert_block do
+ [{}, C.new({})].all? do |i|
+ case i
+ in **a
+ a == {}
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in **a
+ a == {a: 0}
+ end
+ end
+ end
+
+ assert_block do
+ [{}, C.new({})].all? do |i|
+ case i
+ in **;
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in **;
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{}, C.new({})].all? do |i|
+ case i
+ in a:, **b
+ else
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in a:, **b
+ a == 0 && b == {}
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
+ case i
+ in a:, **b
+ a == 0 && b == {b: 1}
+ end
+ end
+ end
+
+ assert_block do
+ case C.new({a: 0})
+ in C(a: 0)
+ true
+ end
+ end
+
+ assert_block do
+ case {a: 0}
+ in C(a: 0)
+ else
+ true
+ end
+ end
+
+ assert_block do
+ case C.new({a: 0})
+ in C[a: 0]
+ true
+ end
+ end
+
+ assert_block do
+ case {a: 0}
+ in C[a: 0]
+ else
+ true
+ end
+ end
+
+ assert_block do
+ [{}, C.new({})].all? do |i|
+ case i
+ in {a: 0}
+ else
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in {a: 0}
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0, b: 1}, C.new({a: 0, b: 1})].all? do |i|
+ case i
+ in {a: 0}
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{}, C.new({})].all? do |i|
+ case i
+ in {}
+ true
+ end
+ end
+ end
+
+ assert_block do
+ [{a: 0}, C.new({a: 0})].all? do |i|
+ case i
+ in {}
+ else
+ true
+ end
+ end
+ end
+
+ assert_syntax_error(%q{
+ case _
+ in a:, a:
+ end
+ }, /duplicated key name/)
+
+ assert_syntax_error(%q{
+ case _
+ in a?:
+ end
+ }, /key must be valid as local variables/)
+
+ assert_block do
+ case {a?: true}
+ in a?: true
+ true
+ end
+ end
+
+ assert_syntax_error(%q{
+ case _
+ in "a-b":
+ end
+ }, /key must be valid as local variables/)
+
+ assert_block do
+ case {"a-b": true}
+ in "a-b": true
+ true
+ end
+ end
+
+ assert_syntax_error(%q{
+ case _
+ in "#{a}": a
+ end
+ }, /symbol literal with interpolation is not allowed/)
+
+ assert_syntax_error(%q{
+ case _
+ in "#{a}":
+ end
+ }, /symbol literal with interpolation is not allowed/)
+ end
+
+ def test_paren
+ assert_block do
+ case 0
+ in (0)
+ true
+ end
+ end
+ end
+
+ def test_invalid_syntax
+ assert_syntax_error(%q{
+ case 0
+ in a, b:
+ end
+ }, /unexpected/)
+
+ assert_syntax_error(%q{
+ case 0
+ in [a:]
+ end
+ }, /unexpected/)
+
+ assert_syntax_error(%q{
+ case 0
+ in {a}
+ end
+ }, /unexpected/)
+
+ assert_syntax_error(%q{
+ case 0
+ in {0 => a}
+ end
+ }, /unexpected/)
+ end
+
+ ################################################################
+
+ class CTypeError
+ def deconstruct
+ nil
+ end
+
+ def deconstruct_keys(keys)
+ nil
+ end
+ end
+
+ def test_deconstruct
+ assert_raise(TypeError) do
+ case CTypeError.new
+ in []
+ end
+ end
+ end
+
+ def test_deconstruct_keys
+ assert_raise(TypeError) do
+ case CTypeError.new
+ in {}
+ end
+ end
+
+ assert_block do
+ case {}
+ in {}
+ $keys == nil
+ end
+ end
+
+ assert_block do
+ case C.new({a: 0, b: 0, c: 0})
+ in {a: 0, b:}
+ C.keys == [:a, :b]
+ end
+ end
+
+ assert_block do
+ case C.new({a: 0, b: 0, c: 0})
+ in {a: 0, b:, **}
+ C.keys == [:a, :b]
+ end
+ end
+
+ assert_block do
+ case C.new({a: 0, b: 0, c: 0})
+ in {a: 0, b:, **r}
+ C.keys == nil
+ end
+ end
+
+ assert_block do
+ case C.new({a: 0, b: 0, c: 0})
+ in {**}
+ C.keys == []
+ end
+ end
+
+ assert_block do
+ case C.new({a: 0, b: 0, c: 0})
+ in {**r}
+ C.keys == nil
+ end
+ end
+ end
+
+ ################################################################
+
+ class TestPatternMatchingRefinements < Test::Unit::TestCase
+ class C1
+ def deconstruct
+ [:C1]
+ end
+ end
+
+ class C2
+ end
+
+ module M
+ refine Array do
+ def deconstruct
+ [0]
+ end
+ end
+
+ refine Hash do
+ def deconstruct_keys(_)
+ {a: 0}
+ end
+ end
+
+ refine C2.singleton_class do
+ def ===(obj)
+ obj.kind_of?(C1)
+ end
+ end
+ end
+
+ using M
+
+ def test_refinements
+ assert_block do
+ case []
+ in [0]
+ true
+ end
+ end
+
+ assert_block do
+ case {}
+ in {a: 0}
+ true
+ end
+ end
+
+ assert_block do
+ case C1.new
+ in C2(:C1)
+ true
+ end
+ end
+ end
+ end
+end