@ -23,6 +23,7 @@ import java.util.HashMap;
@@ -23,6 +23,7 @@ import java.util.HashMap;
import java.util.List ;
import java.util.Map ;
import org.junit.jupiter.api.Nested ;
import org.junit.jupiter.api.Test ;
import org.springframework.http.server.PathContainer ;
@ -34,12 +35,303 @@ import static org.assertj.core.api.Assertions.assertThat;
@@ -34,12 +35,303 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException ;
/ * *
* Exercise matching of { @link PathPattern } objects .
* Tests for { @link PathPattern } .
*
* @author Andy Clement
* @author Brian Clozel
* /
class PathPatternTests {
@Nested
class MatchingTests {
@Test
void basicMatching ( ) {
checkMatches ( "" , "" ) ;
checkMatches ( "" , null ) ;
checkNoMatch ( "/abc" , "/" ) ;
checkMatches ( "/" , "/" ) ;
checkNoMatch ( "/" , "/a" ) ;
checkMatches ( "foo/bar/" , "foo/bar/" ) ;
checkNoMatch ( "foo" , "foobar" ) ;
checkMatches ( "/foo/bar" , "/foo/bar" ) ;
checkNoMatch ( "/foo/bar" , "/foo/baz" ) ;
}
@Test
void literalPathElements ( ) {
checkMatches ( "foo" , "foo" ) ;
checkNoMatch ( "foo" , "bar" ) ;
checkNoMatch ( "foo" , "/foo" ) ;
checkNoMatch ( "/foo" , "foo" ) ;
checkMatches ( "/f" , "/f" ) ;
checkMatches ( "/foo" , "/foo" ) ;
checkNoMatch ( "/foo" , "/food" ) ;
checkNoMatch ( "/food" , "/foo" ) ;
checkMatches ( "/foo/" , "/foo/" ) ;
checkMatches ( "/foo/bar/woo" , "/foo/bar/woo" ) ;
checkMatches ( "foo/bar/woo" , "foo/bar/woo" ) ;
}
@Test
void questionMarks ( ) {
checkNoMatch ( "a" , "ab" ) ;
checkMatches ( "/f?o/bar" , "/foo/bar" ) ;
checkNoMatch ( "/foo/b2r" , "/foo/bar" ) ;
checkNoMatch ( "?" , "te" ) ;
checkMatches ( "?" , "a" ) ;
checkMatches ( "???" , "abc" ) ;
checkNoMatch ( "tes?" , "te" ) ;
checkNoMatch ( "tes?" , "tes" ) ;
checkNoMatch ( "tes?" , "testt" ) ;
checkNoMatch ( "tes?" , "tsst" ) ;
checkMatches ( ".?.a" , ".a.a" ) ;
checkNoMatch ( ".?.a" , ".aba" ) ;
checkMatches ( "/f?o/bar" , "/f%20o/bar" ) ;
}
@Test
void multipleSeparatorsInPattern ( ) {
PathPattern pp = parse ( "a//b//c" ) ;
assertThat ( pp . toChainString ( ) ) . isEqualTo ( "Literal(a) Separator(/) Separator(/) Literal(b) Separator(/) Separator(/) Literal(c)" ) ;
assertMatches ( pp , "a//b//c" ) ;
assertThat ( parse ( "a//**" ) . toChainString ( ) ) . isEqualTo ( "Literal(a) Separator(/) WildcardSegments(/**)" ) ;
checkMatches ( "///abc" , "///abc" ) ;
checkNoMatch ( "///abc" , "/abc" ) ;
checkNoMatch ( "//" , "/" ) ;
checkMatches ( "//" , "//" ) ;
checkNoMatch ( "///abc//d/e" , "/abc/d/e" ) ;
checkMatches ( "///abc//d/e" , "///abc//d/e" ) ;
checkNoMatch ( "///abc//{def}//////xyz" , "/abc/foo/xyz" ) ;
checkMatches ( "///abc//{def}//////xyz" , "///abc//p//////xyz" ) ;
}
@Test
void multipleSelectorsInPath ( ) {
checkNoMatch ( "/abc" , "////abc" ) ;
checkNoMatch ( "/" , "//" ) ;
checkNoMatch ( "/abc/def/ghi" , "/abc//def///ghi" ) ;
checkNoMatch ( "/abc" , "////abc" ) ;
checkMatches ( "////abc" , "////abc" ) ;
checkNoMatch ( "/" , "//" ) ;
checkNoMatch ( "/abc//def" , "/abc/def" ) ;
checkNoMatch ( "/abc//def///ghi" , "/abc/def/ghi" ) ;
checkMatches ( "/abc//def///ghi" , "/abc//def///ghi" ) ;
}
@Test
void multipleSeparatorsInPatternAndPath ( ) {
checkNoMatch ( "///one///two///three" , "//one/////two///////three" ) ;
checkMatches ( "//one/////two///////three" , "//one/////two///////three" ) ;
checkNoMatch ( "//one//two//three" , "/one/////two/three" ) ;
checkMatches ( "/one/////two/three" , "/one/////two/three" ) ;
checkCapture ( "///{foo}///bar" , "///one///bar" , "foo" , "one" ) ;
}
@Test
void captureSegmentsAtStart ( ) {
checkMatches ( "/{*foobar}/resource" , "/resource" ) ;
checkNoMatch ( "/{*foobar}/resource" , "/resourceX" ) ;
checkNoMatch ( "/{*foobar}/resource" , "/foobar/resourceX" ) ;
checkMatches ( "/{*foobar}/resource" , "/foobar/resource" ) ;
}
@Test
void captureSegmentsAtEnd ( ) {
checkMatches ( "/resource/{*foobar}" , "/resource" ) ;
checkNoMatch ( "/resource/{*foobar}" , "/resourceX" ) ;
checkNoMatch ( "/resource/{*foobar}" , "/resourceX/foobar" ) ;
checkMatches ( "/resource/{*foobar}" , "/resource/foobar" ) ;
}
@Test
void wildcards ( ) {
checkMatches ( "/*/bar" , "/foo/bar" ) ;
checkNoMatch ( "/*/bar" , "/foo/baz" ) ;
checkNoMatch ( "/*/bar" , "//bar" ) ;
checkMatches ( "/f*/bar" , "/foo/bar" ) ;
checkMatches ( "/*/bar" , "/foo/bar" ) ;
checkMatches ( "a/*" , "a/" ) ;
checkMatches ( "/*" , "/" ) ;
checkMatches ( "/*/bar" , "/foo/bar" ) ;
checkNoMatch ( "/*/bar" , "/foo/baz" ) ;
checkMatches ( "/f*/bar" , "/foo/bar" ) ;
checkMatches ( "/*/bar" , "/foo/bar" ) ;
checkMatches ( "/a*b*c*d/bar" , "/abcd/bar" ) ;
checkMatches ( "*a*" , "testa" ) ;
checkMatches ( "a/*" , "a/" ) ;
checkNoMatch ( "a/*" , "a//" ) ; // no data for *
PathPatternParser ppp = new PathPatternParser ( ) ;
assertThat ( ppp . parse ( "a/*" ) . matches ( toPathContainer ( "a//" ) ) ) . isFalse ( ) ;
checkMatches ( "a/*" , "a/a" ) ;
}
@Test
void wildcardSegmentsStart ( ) {
checkMatches ( "/**/resource" , "/resource" ) ;
checkNoMatch ( "/**/resource" , "/Xresource" ) ;
checkNoMatch ( "/**/resource" , "/foobar/resourceX" ) ;
checkMatches ( "/**/resource" , "/foobar/resource" ) ;
checkMatches ( "/**/resource/test" , "/foo/bar/resource/test" ) ;
checkNoMatch ( "/**/resource/test" , "/foo/bar/resource/t" ) ;
}
@Test
void wildcardSegmentsEnd ( ) {
checkMatches ( "/resource/**" , "/resource" ) ;
checkNoMatch ( "/resource/**" , "/resourceX" ) ;
checkNoMatch ( "/resource/**" , "/resourceX/foobar" ) ;
checkMatches ( "/resource/**" , "/resource/foobar" ) ;
}
@Test
void antPathMatcherTests ( ) {
// test exact matching
checkMatches ( "test" , "test" ) ;
checkMatches ( "/test" , "/test" ) ;
checkMatches ( "https://example.org" , "https://example.org" ) ;
checkNoMatch ( "/test.jpg" , "test.jpg" ) ;
checkNoMatch ( "test" , "/test" ) ;
checkNoMatch ( "/test" , "test" ) ;
// test matching with ?'s
checkMatches ( "t?st" , "test" ) ;
checkMatches ( "??st" , "test" ) ;
checkMatches ( "tes?" , "test" ) ;
checkMatches ( "te??" , "test" ) ;
checkMatches ( "?es?" , "test" ) ;
checkNoMatch ( "tes?" , "tes" ) ;
checkNoMatch ( "tes?" , "testt" ) ;
checkNoMatch ( "tes?" , "tsst" ) ;
// test matching with *'s
checkMatches ( "*" , "test" ) ;
checkMatches ( "test*" , "test" ) ;
checkMatches ( "test*" , "testTest" ) ;
checkMatches ( "test/*" , "test/Test" ) ;
checkMatches ( "test/*" , "test/t" ) ;
checkMatches ( "test/*" , "test/" ) ;
checkMatches ( "*test*" , "AnothertestTest" ) ;
checkMatches ( "*test" , "Anothertest" ) ;
checkMatches ( "*.*" , "test." ) ;
checkMatches ( "*.*" , "test.test" ) ;
checkMatches ( "*.*" , "test.test.test" ) ;
checkMatches ( "test*aaa" , "testblaaaa" ) ;
checkNoMatch ( "test*" , "tst" ) ;
checkNoMatch ( "test*" , "tsttest" ) ;
checkMatches ( "test*" , "test" ) ; // trailing slash is optional
checkNoMatch ( "test*" , "test/t" ) ;
checkNoMatch ( "test/*" , "test" ) ;
checkNoMatch ( "*test*" , "tsttst" ) ;
checkNoMatch ( "*test" , "tsttst" ) ;
checkNoMatch ( "*.*" , "tsttst" ) ;
checkNoMatch ( "test*aaa" , "test" ) ;
checkNoMatch ( "test*aaa" , "testblaaab" ) ;
// test matching with ?'s and /'s
checkMatches ( "/?" , "/a" ) ;
checkMatches ( "/?/a" , "/a/a" ) ;
checkMatches ( "/a/?" , "/a/b" ) ;
checkMatches ( "/??/a" , "/aa/a" ) ;
checkMatches ( "/a/??" , "/a/bb" ) ;
checkMatches ( "/?" , "/a" ) ;
checkMatches ( "/**" , "" ) ;
checkMatches ( "/books/**" , "/books" ) ;
checkMatches ( "/**" , "/testing/testing" ) ;
checkMatches ( "/*/**" , "/testing/testing" ) ;
checkMatches ( "/bla*bla/test" , "/blaXXXbla/test" ) ;
checkMatches ( "/*bla/test" , "/XXXbla/test" ) ;
checkNoMatch ( "/bla*bla/test" , "/blaXXXbl/test" ) ;
checkNoMatch ( "/*bla/test" , "XXXblab/test" ) ;
checkNoMatch ( "/*bla/test" , "XXXbl/test" ) ;
checkNoMatch ( "/????" , "/bala/bla" ) ;
checkMatches ( "/foo/bar/**" , "/foo/bar/" ) ;
checkMatches ( "/{bla}.html" , "/testing.html" ) ;
checkCapture ( "/{bla}.*" , "/testing.html" , "bla" , "testing" ) ;
}
}
@Nested
class VariableCaptureTests {
@Test
void constrainedMatches ( ) {
checkCapture ( "{foo:[0-9]*}" , "123" , "foo" , "123" ) ;
checkNoMatch ( "{foo:[0-9]*}" , "abc" ) ;
checkNoMatch ( "/{foo:[0-9]*}" , "abc" ) ;
checkCapture ( "/*/{foo:....}/**" , "/foo/barg/foo" , "foo" , "barg" ) ;
checkCapture ( "/*/{foo:....}/**" , "/foo/barg/abc/def/ghi" , "foo" , "barg" ) ;
checkNoMatch ( "{foo:....}" , "99" ) ;
checkMatches ( "{foo:..}" , "99" ) ;
checkCapture ( "/{abc:\\{\\}}" , "/{}" , "abc" , "{}" ) ;
checkCapture ( "/{abc:\\[\\]}" , "/[]" , "abc" , "[]" ) ;
checkCapture ( "/{abc:\\\\\\\\}" , "/\\\\" ) ; // this is fun...
}
@Test
void captureSegmentsAtStart ( ) {
checkCapture ( "/{*foobar}/resource" , "/foobar/resource" , "foobar" , "/foobar" ) ;
checkCapture ( "/{*something}/customer" , "/99/customer" , "something" , "/99" ) ;
checkCapture ( "/{*something}/customer" , "/aa/bb/cc/customer" , "something" , "/aa/bb/cc" ) ;
checkCapture ( "/{*something}/customer" , "/customer" , "something" , "" ) ;
checkCapture ( "/{*something}/customer" , "//////99/customer" , "something" , "//////99" ) ;
}
@Test
void captureSegmentsAtEnd ( ) {
checkCapture ( "/resource/{*foobar}" , "/resource/foobar" , "foobar" , "/foobar" ) ;
checkCapture ( "/customer/{*something}" , "/customer/99" , "something" , "/99" ) ;
checkCapture ( "/customer/{*something}" , "/customer/aa/bb/cc" , "something" ,
"/aa/bb/cc" ) ;
checkCapture ( "/customer/{*something}" , "/customer/" , "something" , "/" ) ;
checkCapture ( "/customer/////{*something}" , "/customer/////" , "something" , "/" ) ;
checkCapture ( "/customer/////{*something}" , "/customer//////" , "something" , "//" ) ;
checkCapture ( "/customer//////{*something}" , "/customer//////99" , "something" , "/99" ) ;
checkCapture ( "/customer//////{*something}" , "/customer//////99" , "something" , "/99" ) ;
checkCapture ( "/customer/{*something}" , "/customer" , "something" , "" ) ;
checkCapture ( "/{*something}" , "" , "something" , "" ) ;
checkCapture ( "/customer/{*something}" , "/customer//////99" , "something" , "//////99" ) ;
}
@Test
void encodingAndBoundVariablesCapturePathElement ( ) {
checkCapture ( "{var}" , "f%20o" , "var" , "f o" ) ;
checkCapture ( "{var1}/{var2}" , "f%20o/f%7Co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "{var1}/{var2}" , "f%20o/f%7co" , "var1" , "f o" , "var2" , "f|o" ) ; // lower case encoding
checkCapture ( "{var:foo}" , "foo" , "var" , "foo" ) ;
checkCapture ( "{var:f o}" , "f%20o" , "var" , "f o" ) ; // constraint is expressed in non encoded form
checkCapture ( "{var:f.o}" , "f%20o" , "var" , "f o" ) ;
checkCapture ( "{var:f\\|o}" , "f%7co" , "var" , "f|o" ) ;
checkCapture ( "{var:.*}" , "x\ny" , "var" , "x\ny" ) ;
}
@Test
void encodingAndBoundVariablesCaptureTheRestPathElement ( ) {
checkCapture ( "/{*var}" , "/f%20o" , "var" , "/f o" ) ;
checkCapture ( "{var1}/{*var2}" , "f%20o/f%7Co" , "var1" , "f o" , "var2" , "/f|o" ) ;
checkCapture ( "/{*var}" , "/foo" , "var" , "/foo" ) ;
checkCapture ( "/{*var}" , "/f%20o" , "var" , "/f o" ) ;
checkCapture ( "/{*var}" , "/f%20o" , "var" , "/f o" ) ;
checkCapture ( "/{*var}" , "/f%7co" , "var" , "/f|o" ) ;
}
@Test
void encodingAndBoundVariablesRegexPathElement ( ) {
checkCapture ( "/{var1:f o}_ _{var2}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1}_{var2}" , "/f%20o_foo" , "var1" , "f o" , "var2" , "foo" ) ;
checkCapture ( "/{var1}_ _{var2}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1}_ _{var2:f\\|o}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1:f o}_ _{var2:f\\|o}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1:f o}_ _{var2:f\\|o}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1}_{var2}" , "/f\noo_foo" , "var1" , "f\noo" , "var2" , "foo" ) ;
}
}
@Test
void pathContainer ( ) {
assertThat ( elementsToString ( toPathContainer ( "/abc/def" ) . elements ( ) ) ) . isEqualTo ( "[/][abc][/][def]" ) ;
@ -62,250 +354,8 @@ class PathPatternTests {
@@ -62,250 +354,8 @@ class PathPatternTests {
assertThat ( parser . parse ( "/foo/bar" ) . hasPatternSyntax ( ) ) . isFalse ( ) ;
}
@Test
void matching_LiteralPathElement ( ) {
checkMatches ( "foo" , "foo" ) ;
checkNoMatch ( "foo" , "bar" ) ;
checkNoMatch ( "foo" , "/foo" ) ;
checkNoMatch ( "/foo" , "foo" ) ;
checkMatches ( "/f" , "/f" ) ;
checkMatches ( "/foo" , "/foo" ) ;
checkNoMatch ( "/foo" , "/food" ) ;
checkNoMatch ( "/food" , "/foo" ) ;
checkMatches ( "/foo/" , "/foo/" ) ;
checkMatches ( "/foo/bar/woo" , "/foo/bar/woo" ) ;
checkMatches ( "foo/bar/woo" , "foo/bar/woo" ) ;
}
@Test
void basicMatching ( ) {
checkMatches ( "" , "" ) ;
checkMatches ( "" , "/" ) ;
checkMatches ( "" , null ) ;
checkNoMatch ( "/abc" , "/" ) ;
checkMatches ( "/" , "/" ) ;
checkNoMatch ( "/" , "/a" ) ;
checkMatches ( "foo/bar/" , "foo/bar/" ) ;
checkNoMatch ( "foo" , "foobar" ) ;
checkMatches ( "/foo/bar" , "/foo/bar" ) ;
checkNoMatch ( "/foo/bar" , "/foo/baz" ) ;
}
private void assertMatches ( PathPattern pp , String path ) {
assertThat ( pp . matches ( toPathContainer ( path ) ) ) . isTrue ( ) ;
}
private void assertNoMatch ( PathPattern pp , String path ) {
assertThat ( pp . matches ( toPathContainer ( path ) ) ) . isFalse ( ) ;
}
@SuppressWarnings ( "deprecation" )
@Test
void optionalTrailingSeparators ( ) {
PathPattern pp ;
// LiteralPathElement
pp = parse ( "/resource" ) ;
assertMatches ( pp , "/resource" ) ;
assertMatches ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
pp = parse ( "/resource/" ) ;
assertNoMatch ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
pp = parse ( "res?urce" ) ;
assertNoMatch ( pp , "resource//" ) ;
// SingleCharWildcardPathElement
pp = parse ( "/res?urce" ) ;
assertMatches ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
pp = parse ( "/res?urce/" ) ;
assertNoMatch ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
// CaptureVariablePathElement
pp = parse ( "/{var}" ) ;
assertMatches ( pp , "/resource" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource" ) ) . getUriVariables ( ) ) . containsEntry ( "var" , "resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource/" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"resource"
) ;
assertNoMatch ( pp , "/resource//" ) ;
pp = parse ( "/{var}/" ) ;
assertNoMatch ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource/" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"resource"
) ;
assertNoMatch ( pp , "/resource//" ) ;
// CaptureTheRestPathElement
pp = parse ( "/{*var}" ) ;
assertMatches ( pp , "/resource" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"/resource"
) ;
assertMatches ( pp , "/resource/" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource/" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"/resource/"
) ;
assertMatches ( pp , "/resource//" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource//" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"/resource//"
) ;
assertMatches ( pp , "//resource//" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "//resource//" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"//resource//"
) ;
// WildcardTheRestPathElement
pp = parse ( "/**" ) ;
assertMatches ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertMatches ( pp , "/resource//" ) ;
assertMatches ( pp , "//resource//" ) ;
// WildcardPathElement
pp = parse ( "/*" ) ;
assertMatches ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
pp = parse ( "/*/" ) ;
assertNoMatch ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
// RegexPathElement
pp = parse ( "/{var1}_{var2}" ) ;
assertMatches ( pp , "/res1_res2" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2" ) ) . getUriVariables ( ) ) . containsEntry ( "var1" , "res1" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2" ) ) . getUriVariables ( ) ) . containsEntry ( "var2" , "res2" ) ;
assertMatches ( pp , "/res1_res2/" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2/" ) ) . getUriVariables ( ) ) . containsEntry ( "var1" , "res1" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2/" ) ) . getUriVariables ( ) ) . containsEntry ( "var2" , "res2" ) ;
assertNoMatch ( pp , "/res1_res2//" ) ;
pp = parse ( "/{var1}_{var2}/" ) ;
assertNoMatch ( pp , "/res1_res2" ) ;
assertMatches ( pp , "/res1_res2/" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2/" ) ) . getUriVariables ( ) ) . containsEntry ( "var1" , "res1" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2/" ) ) . getUriVariables ( ) ) . containsEntry ( "var2" , "res2" ) ;
assertNoMatch ( pp , "/res1_res2//" ) ;
pp = parse ( "/{var1}*" ) ;
assertMatches ( pp , "/a" ) ;
assertMatches ( pp , "/a/" ) ;
assertNoMatch ( pp , "/" ) ; // no characters for var1
assertNoMatch ( pp , "//" ) ; // no characters for var1
// Now with trailing matching turned OFF
PathPatternParser parser = new PathPatternParser ( ) ;
parser . setMatchOptionalTrailingSeparator ( false ) ;
// LiteralPathElement
pp = parser . parse ( "/resource" ) ;
assertMatches ( pp , "/resource" ) ;
assertNoMatch ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
pp = parser . parse ( "/resource/" ) ;
assertNoMatch ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
// SingleCharWildcardPathElement
pp = parser . parse ( "/res?urce" ) ;
assertMatches ( pp , "/resource" ) ;
assertNoMatch ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
pp = parser . parse ( "/res?urce/" ) ;
assertNoMatch ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
// CaptureVariablePathElement
pp = parser . parse ( "/{var}" ) ;
assertMatches ( pp , "/resource" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource" ) ) . getUriVariables ( ) ) . containsEntry ( "var" , "resource" ) ;
assertNoMatch ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
pp = parser . parse ( "/{var}/" ) ;
assertNoMatch ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource/" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"resource"
) ;
assertNoMatch ( pp , "/resource//" ) ;
// CaptureTheRestPathElement
pp = parser . parse ( "/{*var}" ) ;
assertMatches ( pp , "/resource" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"/resource"
) ;
assertMatches ( pp , "/resource/" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource/" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"/resource/"
) ;
assertMatches ( pp , "/resource//" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/resource//" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"/resource//"
) ;
assertMatches ( pp , "//resource//" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "//resource//" ) ) . getUriVariables ( ) ) . containsEntry (
"var" ,
"//resource//"
) ;
// WildcardTheRestPathElement
pp = parser . parse ( "/**" ) ;
assertMatches ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertMatches ( pp , "/resource//" ) ;
assertMatches ( pp , "//resource//" ) ;
// WildcardPathElement
pp = parser . parse ( "/*" ) ;
assertMatches ( pp , "/resource" ) ;
assertNoMatch ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
pp = parser . parse ( "/*/" ) ;
assertNoMatch ( pp , "/resource" ) ;
assertMatches ( pp , "/resource/" ) ;
assertNoMatch ( pp , "/resource//" ) ;
// RegexPathElement
pp = parser . parse ( "/{var1}_{var2}" ) ;
assertMatches ( pp , "/res1_res2" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2" ) ) . getUriVariables ( ) ) . containsEntry ( "var1" , "res1" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2" ) ) . getUriVariables ( ) ) . containsEntry ( "var2" , "res2" ) ;
assertNoMatch ( pp , "/res1_res2/" ) ;
assertNoMatch ( pp , "/res1_res2//" ) ;
pp = parser . parse ( "/{var1}_{var2}/" ) ;
assertNoMatch ( pp , "/res1_res2" ) ;
assertMatches ( pp , "/res1_res2/" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2/" ) ) . getUriVariables ( ) ) . containsEntry ( "var1" , "res1" ) ;
assertThat ( pp . matchAndExtract ( toPathContainer ( "/res1_res2/" ) ) . getUriVariables ( ) ) . containsEntry ( "var2" , "res2" ) ;
assertNoMatch ( pp , "/res1_res2//" ) ;
pp = parser . parse ( "/{var1}*" ) ;
assertMatches ( pp , "/a" ) ;
assertNoMatch ( pp , "/a/" ) ;
assertNoMatch ( pp , "/" ) ; // no characters for var1
assertNoMatch ( pp , "//" ) ; // no characters for var1
}
@Test
void pathRemainderBasicCases_spr15336 ( ) {
@Test // SPR-15336
void pathRemainderBasicCases ( ) {
// Cover all PathElement kinds
assertThat ( getPathRemaining ( "/foo" , "/foo/bar" ) . getPathRemaining ( ) . value ( ) ) . isEqualTo ( "/bar" ) ;
assertThat ( getPathRemaining ( "/foo" , "/foo/" ) . getPathRemaining ( ) . value ( ) ) . isEqualTo ( "/" ) ;
@ -325,41 +375,8 @@ class PathPatternTests {
@@ -325,41 +375,8 @@ class PathPatternTests {
assertThat ( getPathRemaining ( "/foo//" , "/foo///bar" ) . getPathRemaining ( ) . value ( ) ) . isEqualTo ( "/bar" ) ;
}
@Test
void encodingAndBoundVariablesCapturePathElement ( ) {
checkCapture ( "{var}" , "f%20o" , "var" , "f o" ) ;
checkCapture ( "{var1}/{var2}" , "f%20o/f%7Co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "{var1}/{var2}" , "f%20o/f%7co" , "var1" , "f o" , "var2" , "f|o" ) ; // lower case encoding
checkCapture ( "{var:foo}" , "foo" , "var" , "foo" ) ;
checkCapture ( "{var:f o}" , "f%20o" , "var" , "f o" ) ; // constraint is expressed in non encoded form
checkCapture ( "{var:f.o}" , "f%20o" , "var" , "f o" ) ;
checkCapture ( "{var:f\\|o}" , "f%7co" , "var" , "f|o" ) ;
checkCapture ( "{var:.*}" , "x\ny" , "var" , "x\ny" ) ;
}
@Test
void encodingAndBoundVariablesCaptureTheRestPathElement ( ) {
checkCapture ( "/{*var}" , "/f%20o" , "var" , "/f o" ) ;
checkCapture ( "{var1}/{*var2}" , "f%20o/f%7Co" , "var1" , "f o" , "var2" , "/f|o" ) ;
checkCapture ( "/{*var}" , "/foo" , "var" , "/foo" ) ;
checkCapture ( "/{*var}" , "/f%20o" , "var" , "/f o" ) ;
checkCapture ( "/{*var}" , "/f%20o" , "var" , "/f o" ) ;
checkCapture ( "/{*var}" , "/f%7co" , "var" , "/f|o" ) ;
}
@Test
void encodingAndBoundVariablesRegexPathElement ( ) {
checkCapture ( "/{var1:f o}_ _{var2}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1}_{var2}" , "/f%20o_foo" , "var1" , "f o" , "var2" , "foo" ) ;
checkCapture ( "/{var1}_ _{var2}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1}_ _{var2:f\\|o}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1:f o}_ _{var2:f\\|o}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1:f o}_ _{var2:f\\|o}" , "/f%20o_%20_f%7co" , "var1" , "f o" , "var2" , "f|o" ) ;
checkCapture ( "/{var1}_{var2}" , "/f\noo_foo" , "var1" , "f\noo" , "var2" , "foo" ) ;
}
@Test
void pathRemainingCornerCases_spr15336 ( ) {
@Test // SPR-15336
void pathRemainingCornerCases ( ) {
// No match when the literal path element is a longer form of the segment in the pattern
assertThat ( parse ( "/foo" ) . matchStartOfPath ( toPathContainer ( "/footastic/bar" ) ) ) . isNull ( ) ;
assertThat ( parse ( "/f?o" ) . matchStartOfPath ( toPathContainer ( "/footastic/bar" ) ) ) . isNull ( ) ;
@ -372,6 +389,11 @@ class PathPatternTests {
@@ -372,6 +389,11 @@ class PathPatternTests {
assertThat ( parse ( "/resource/**" )
. matchStartOfPath ( toPathContainer ( "/resource" ) ) . getPathRemaining ( ) . value ( ) ) . isEmpty ( ) ;
assertThat ( parse ( "/**/resource" )
. matchStartOfPath ( toPathContainer ( "/test/resource" ) ) . getPathRemaining ( ) . value ( ) ) . isEmpty ( ) ;
assertThat ( parse ( "/**/resource" )
. matchStartOfPath ( toPathContainer ( "/test/resource/other" ) ) . getPathRemaining ( ) . value ( ) ) . isEqualTo ( "/other" ) ;
// Similar to above for the capture-the-rest variant
assertThat ( parse ( "/resource/{*foo}" ) . matchStartOfPath ( toPathContainer ( "/resourceX" ) ) ) . isNull ( ) ;
assertThat ( parse ( "/resource/{*foo}" )
@ -400,195 +422,8 @@ class PathPatternTests {
@@ -400,195 +422,8 @@ class PathPatternTests {
assertThat ( parse ( "" ) . matchStartOfPath ( toPathContainer ( "" ) ) . getPathRemaining ( ) . value ( ) ) . isEmpty ( ) ;
}
@Test
void questionMarks ( ) {
checkNoMatch ( "a" , "ab" ) ;
checkMatches ( "/f?o/bar" , "/foo/bar" ) ;
checkNoMatch ( "/foo/b2r" , "/foo/bar" ) ;
checkNoMatch ( "?" , "te" ) ;
checkMatches ( "?" , "a" ) ;
checkMatches ( "???" , "abc" ) ;
checkNoMatch ( "tes?" , "te" ) ;
checkNoMatch ( "tes?" , "tes" ) ;
checkNoMatch ( "tes?" , "testt" ) ;
checkNoMatch ( "tes?" , "tsst" ) ;
checkMatches ( ".?.a" , ".a.a" ) ;
checkNoMatch ( ".?.a" , ".aba" ) ;
checkMatches ( "/f?o/bar" , "/f%20o/bar" ) ;
}
@Test
void captureTheRest ( ) {
checkMatches ( "/resource/{*foobar}" , "/resource" ) ;
checkNoMatch ( "/resource/{*foobar}" , "/resourceX" ) ;
checkNoMatch ( "/resource/{*foobar}" , "/resourceX/foobar" ) ;
checkMatches ( "/resource/{*foobar}" , "/resource/foobar" ) ;
checkCapture ( "/resource/{*foobar}" , "/resource/foobar" , "foobar" , "/foobar" ) ;
checkCapture ( "/customer/{*something}" , "/customer/99" , "something" , "/99" ) ;
checkCapture ( "/customer/{*something}" , "/customer/aa/bb/cc" , "something" ,
"/aa/bb/cc" ) ;
checkCapture ( "/customer/{*something}" , "/customer/" , "something" , "/" ) ;
checkCapture ( "/customer/////{*something}" , "/customer/////" , "something" , "/" ) ;
checkCapture ( "/customer/////{*something}" , "/customer//////" , "something" , "//" ) ;
checkCapture ( "/customer//////{*something}" , "/customer//////99" , "something" , "/99" ) ;
checkCapture ( "/customer//////{*something}" , "/customer//////99" , "something" , "/99" ) ;
checkCapture ( "/customer/{*something}" , "/customer" , "something" , "" ) ;
checkCapture ( "/{*something}" , "" , "something" , "" ) ;
checkCapture ( "/customer/{*something}" , "/customer//////99" , "something" , "//////99" ) ;
}
@Test
void multipleSeparatorsInPattern ( ) {
PathPattern pp = parse ( "a//b//c" ) ;
assertThat ( pp . toChainString ( ) ) . isEqualTo ( "Literal(a) Separator(/) Separator(/) Literal(b) Separator(/) Separator(/) Literal(c)" ) ;
assertMatches ( pp , "a//b//c" ) ;
assertThat ( parse ( "a//**" ) . toChainString ( ) ) . isEqualTo ( "Literal(a) Separator(/) WildcardTheRest(/**)" ) ;
checkMatches ( "///abc" , "///abc" ) ;
checkNoMatch ( "///abc" , "/abc" ) ;
checkNoMatch ( "//" , "/" ) ;
checkMatches ( "//" , "//" ) ;
checkNoMatch ( "///abc//d/e" , "/abc/d/e" ) ;
checkMatches ( "///abc//d/e" , "///abc//d/e" ) ;
checkNoMatch ( "///abc//{def}//////xyz" , "/abc/foo/xyz" ) ;
checkMatches ( "///abc//{def}//////xyz" , "///abc//p//////xyz" ) ;
}
@Test
void multipleSelectorsInPath ( ) {
checkNoMatch ( "/abc" , "////abc" ) ;
checkNoMatch ( "/" , "//" ) ;
checkNoMatch ( "/abc/def/ghi" , "/abc//def///ghi" ) ;
checkNoMatch ( "/abc" , "////abc" ) ;
checkMatches ( "////abc" , "////abc" ) ;
checkNoMatch ( "/" , "//" ) ;
checkNoMatch ( "/abc//def" , "/abc/def" ) ;
checkNoMatch ( "/abc//def///ghi" , "/abc/def/ghi" ) ;
checkMatches ( "/abc//def///ghi" , "/abc//def///ghi" ) ;
}
@Test
void multipleSeparatorsInPatternAndPath ( ) {
checkNoMatch ( "///one///two///three" , "//one/////two///////three" ) ;
checkMatches ( "//one/////two///////three" , "//one/////two///////three" ) ;
checkNoMatch ( "//one//two//three" , "/one/////two/three" ) ;
checkMatches ( "/one/////two/three" , "/one/////two/three" ) ;
checkCapture ( "///{foo}///bar" , "///one///bar" , "foo" , "one" ) ;
}
@SuppressWarnings ( "deprecation" )
@Test
void wildcards ( ) {
checkMatches ( "/*/bar" , "/foo/bar" ) ;
checkNoMatch ( "/*/bar" , "/foo/baz" ) ;
checkNoMatch ( "/*/bar" , "//bar" ) ;
checkMatches ( "/f*/bar" , "/foo/bar" ) ;
checkMatches ( "/*/bar" , "/foo/bar" ) ;
checkMatches ( "a/*" , "a/" ) ;
checkMatches ( "/*" , "/" ) ;
checkMatches ( "/*/bar" , "/foo/bar" ) ;
checkNoMatch ( "/*/bar" , "/foo/baz" ) ;
checkMatches ( "/f*/bar" , "/foo/bar" ) ;
checkMatches ( "/*/bar" , "/foo/bar" ) ;
checkMatches ( "/a*b*c*d/bar" , "/abcd/bar" ) ;
checkMatches ( "*a*" , "testa" ) ;
checkMatches ( "a/*" , "a/" ) ;
checkNoMatch ( "a/*" , "a//" ) ; // no data for *
checkMatches ( "a/*" , "a/a/" ) ; // trailing slash, so is allowed
PathPatternParser ppp = new PathPatternParser ( ) ;
ppp . setMatchOptionalTrailingSeparator ( false ) ;
assertThat ( ppp . parse ( "a/*" ) . matches ( toPathContainer ( "a//" ) ) ) . isFalse ( ) ;
checkMatches ( "a/*" , "a/a" ) ;
checkMatches ( "a/*" , "a/a/" ) ; // trailing slash is optional
checkMatches ( "/resource/**" , "/resource" ) ;
checkNoMatch ( "/resource/**" , "/resourceX" ) ;
checkNoMatch ( "/resource/**" , "/resourceX/foobar" ) ;
checkMatches ( "/resource/**" , "/resource/foobar" ) ;
}
@Test
void constrainedMatches ( ) {
checkCapture ( "{foo:[0-9]*}" , "123" , "foo" , "123" ) ;
checkNoMatch ( "{foo:[0-9]*}" , "abc" ) ;
checkNoMatch ( "/{foo:[0-9]*}" , "abc" ) ;
checkCapture ( "/*/{foo:....}/**" , "/foo/barg/foo" , "foo" , "barg" ) ;
checkCapture ( "/*/{foo:....}/**" , "/foo/barg/abc/def/ghi" , "foo" , "barg" ) ;
checkNoMatch ( "{foo:....}" , "99" ) ;
checkMatches ( "{foo:..}" , "99" ) ;
checkCapture ( "/{abc:\\{\\}}" , "/{}" , "abc" , "{}" ) ;
checkCapture ( "/{abc:\\[\\]}" , "/[]" , "abc" , "[]" ) ;
checkCapture ( "/{abc:\\\\\\\\}" , "/\\\\" ) ; // this is fun...
}
@Test
void antPathMatcherTests ( ) {
// test exact matching
checkMatches ( "test" , "test" ) ;
checkMatches ( "/test" , "/test" ) ;
checkMatches ( "https://example.org" , "https://example.org" ) ;
checkNoMatch ( "/test.jpg" , "test.jpg" ) ;
checkNoMatch ( "test" , "/test" ) ;
checkNoMatch ( "/test" , "test" ) ;
// test matching with ?'s
checkMatches ( "t?st" , "test" ) ;
checkMatches ( "??st" , "test" ) ;
checkMatches ( "tes?" , "test" ) ;
checkMatches ( "te??" , "test" ) ;
checkMatches ( "?es?" , "test" ) ;
checkNoMatch ( "tes?" , "tes" ) ;
checkNoMatch ( "tes?" , "testt" ) ;
checkNoMatch ( "tes?" , "tsst" ) ;
// test matching with *'s
checkMatches ( "*" , "test" ) ;
checkMatches ( "test*" , "test" ) ;
checkMatches ( "test*" , "testTest" ) ;
checkMatches ( "test/*" , "test/Test" ) ;
checkMatches ( "test/*" , "test/t" ) ;
checkMatches ( "test/*" , "test/" ) ;
checkMatches ( "*test*" , "AnothertestTest" ) ;
checkMatches ( "*test" , "Anothertest" ) ;
checkMatches ( "*.*" , "test." ) ;
checkMatches ( "*.*" , "test.test" ) ;
checkMatches ( "*.*" , "test.test.test" ) ;
checkMatches ( "test*aaa" , "testblaaaa" ) ;
checkNoMatch ( "test*" , "tst" ) ;
checkNoMatch ( "test*" , "tsttest" ) ;
checkMatches ( "test*" , "test/" ) ; // trailing slash is optional
checkMatches ( "test*" , "test" ) ; // trailing slash is optional
checkNoMatch ( "test*" , "test/t" ) ;
checkNoMatch ( "test/*" , "test" ) ;
checkNoMatch ( "*test*" , "tsttst" ) ;
checkNoMatch ( "*test" , "tsttst" ) ;
checkNoMatch ( "*.*" , "tsttst" ) ;
checkNoMatch ( "test*aaa" , "test" ) ;
checkNoMatch ( "test*aaa" , "testblaaab" ) ;
// test matching with ?'s and /'s
checkMatches ( "/?" , "/a" ) ;
checkMatches ( "/?/a" , "/a/a" ) ;
checkMatches ( "/a/?" , "/a/b" ) ;
checkMatches ( "/??/a" , "/aa/a" ) ;
checkMatches ( "/a/??" , "/a/bb" ) ;
checkMatches ( "/?" , "/a" ) ;
checkMatches ( "/**" , "" ) ;
checkMatches ( "/books/**" , "/books" ) ;
checkMatches ( "/**" , "/testing/testing" ) ;
checkMatches ( "/*/**" , "/testing/testing" ) ;
checkMatches ( "/bla*bla/test" , "/blaXXXbla/test" ) ;
checkMatches ( "/*bla/test" , "/XXXbla/test" ) ;
checkNoMatch ( "/bla*bla/test" , "/blaXXXbl/test" ) ;
checkNoMatch ( "/*bla/test" , "XXXblab/test" ) ;
checkNoMatch ( "/*bla/test" , "XXXbl/test" ) ;
checkNoMatch ( "/????" , "/bala/bla" ) ;
checkMatches ( "/foo/bar/**" , "/foo/bar/" ) ;
checkMatches ( "/{bla}.html" , "/testing.html" ) ;
checkCapture ( "/{bla}.*" , "/testing.html" , "bla" , "testing" ) ;
}
@Test
void pathRemainingEnhancements_spr15419 ( ) {
@Test // SPR-15149
void pathRemainingEnhancements ( ) {
PathPattern pp ;
PathPattern . PathRemainingMatchInfo pri ;
// It would be nice to partially match a path and get any bound variables in one step
@ -705,8 +540,8 @@ class PathPatternTests {
@@ -705,8 +540,8 @@ class PathPatternTests {
assertMatches ( p , "bAb" ) ;
}
@Test
void extractPathWithinPattern_spr15259 ( ) {
@Test // SPR-15259
void extractPathWithinPatternWildards ( ) {
checkExtractPathWithinPattern ( "/**" , "//" , "" ) ;
checkExtractPathWithinPattern ( "/**" , "/" , "" ) ;
checkExtractPathWithinPattern ( "/**" , "" , "" ) ;
@ -763,9 +598,8 @@ class PathPatternTests {
@@ -763,9 +598,8 @@ class PathPatternTests {
assertThat ( result . elements ( ) ) . hasSize ( 3 ) ;
}
@Test
@SuppressWarnings ( "deprecation" )
public void extractUriTemplateVariables_spr15264 ( ) {
@Test // SPR-15264
public void extractUriTemplateVariables ( ) {
PathPattern pp ;
pp = new PathPatternParser ( ) . parse ( "/{foo}" ) ;
assertMatches ( pp , "/abc" ) ;
@ -805,7 +639,6 @@ class PathPatternTests {
@@ -805,7 +639,6 @@ class PathPatternTests {
// Only patterns not capturing variables cannot match against just /
PathPatternParser ppp = new PathPatternParser ( ) ;
ppp . setMatchOptionalTrailingSeparator ( true ) ;
pp = ppp . parse ( "/****" ) ;
assertMatches ( pp , "/abcdef" ) ;
assertMatches ( pp , "/" ) ;
@ -822,10 +655,7 @@ class PathPatternTests {
@@ -822,10 +655,7 @@ class PathPatternTests {
Map < String , String > vars = new AntPathMatcher ( ) . extractUriTemplateVariables ( "/{foo}{bar}" , "/a" ) ;
assertThat ( vars ) . containsEntry ( "foo" , "a" ) ;
assertThat ( vars . get ( "bar" ) ) . isEmpty ( ) ;
}
@Test
void extractUriTemplateVariables ( ) {
assertMatches ( parse ( "{hotel}" ) , "1" ) ;
assertMatches ( parse ( "/hotels/{hotel}" ) , "/hotels/1" ) ;
checkCapture ( "/hotels/{hotel}" , "/hotels/1" , "hotel" , "1" ) ;
@ -900,8 +730,8 @@ class PathPatternTests {
@@ -900,8 +730,8 @@ class PathPatternTests {
PathPatternParser ppp = new PathPatternParser ( ) ;
PathPattern pathPattern = ppp . parse ( "/web/{id:foo(bar)?}_{goo}" ) ;
assertThatIllegalArgumentException ( ) . isThrownBy ( ( ) - >
matchAndExtract ( pathPattern , "/web/foobar_goo" ) )
. withMessageContaining ( "The number of capturing groups in the pattern" ) ;
matchAndExtract ( pathPattern , "/web/foobar_goo" ) )
. withMessageContaining ( "The number of capturing groups in the pattern" ) ;
}
@Test
@ -1003,8 +833,8 @@ class PathPatternTests {
@@ -1003,8 +833,8 @@ class PathPatternTests {
parse ( "/hotels/{hotel}/booking" ) ) ) . isEqualTo ( 1 ) ;
assertThat ( comparator . compare (
parse ( "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}" ) ,
parse ( "/**" ) ) ) . isEqualTo ( - 1 ) ;
parse ( "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}" ) ,
parse ( "/**" ) ) ) . isEqualTo ( - 1 ) ;
assertThat ( comparator . compare ( parse ( "/**" ) ,
parse ( "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}" ) ) ) . isEqualTo ( 1 ) ;
assertThat ( comparator . compare ( parse ( "/**" ) , parse ( "/**" ) ) ) . isEqualTo ( 0 ) ;
@ -1021,8 +851,8 @@ class PathPatternTests {
@@ -1021,8 +851,8 @@ class PathPatternTests {
// SPR-6741
assertThat ( comparator . compare (
parse ( "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}" ) ,
parse ( "/hotels/**" ) ) ) . isEqualTo ( - 1 ) ;
parse ( "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}" ) ,
parse ( "/hotels/**" ) ) ) . isEqualTo ( - 1 ) ;
assertThat ( comparator . compare ( parse ( "/hotels/**" ) ,
parse ( "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}" ) ) ) . isEqualTo ( 1 ) ;
assertThat ( comparator . compare ( parse ( "/hotels/foo/bar/**" ) ,
@ -1214,6 +1044,43 @@ class PathPatternTests {
@@ -1214,6 +1044,43 @@ class PathPatternTests {
assertThat ( result ) . isNotNull ( ) ;
}
@Test
void regexPathElementPatterns ( ) {
PathPatternParser pp = new PathPatternParser ( ) ;
PathPattern pattern = pp . parse ( "/{var:\\\\}" ) ;
assertMatches ( pattern , "/\\" ) ;
pattern = pp . parse ( "/{var:\\/}" ) ;
assertNoMatch ( pattern , "/aaa" ) ;
pattern = pp . parse ( "/{var:[^\\/]*}" ) ;
PathPattern . PathMatchInfo result = matchAndExtract ( pattern , "/foo" ) ;
assertThat ( result . getUriVariables ( ) . get ( "var" ) ) . isEqualTo ( "foo" ) ;
pattern = pp . parse ( "/{var:\\[*}" ) ;
result = matchAndExtract ( pattern , "/[[[" ) ;
assertThat ( result . getUriVariables ( ) . get ( "var" ) ) . isEqualTo ( "[[[" ) ;
pattern = pp . parse ( "/{var:[\\{]*}" ) ;
result = matchAndExtract ( pattern , "/{{{" ) ;
assertThat ( result . getUriVariables ( ) . get ( "var" ) ) . isEqualTo ( "{{{" ) ;
pattern = pp . parse ( "/{var:[\\}]*}" ) ;
result = matchAndExtract ( pattern , "/}}}" ) ;
assertThat ( result . getUriVariables ( ) . get ( "var" ) ) . isEqualTo ( "}}}" ) ;
}
private void assertMatches ( PathPattern pp , String path ) {
assertThat ( pp . matches ( toPathContainer ( path ) ) ) . isTrue ( ) ;
}
private void assertNoMatch ( PathPattern pp , String path ) {
assertThat ( pp . matches ( toPathContainer ( path ) ) ) . isFalse ( ) ;
}
private PathPattern . PathMatchInfo matchAndExtract ( String pattern , String path ) {
return parse ( pattern ) . matchAndExtract ( PathPatternTests . toPathContainer ( path ) ) ;
}