blob: d256f1ca8155d8fb789f2149124acea39e1f63ea [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package groovy
import org.junit.Test
import java.util.regex.Matcher
import java.util.regex.Pattern
import static groovy.test.GroovyAssert.shouldFail
/**
* Tests Groovy's regular expression syntax and DGM methods.
*/
final class RegularExpressionsTest {
@Test
void testMatchOperator() {
assert "cheese" ==~ "cheese"
assert !("cheesecheese" ==~ "cheese")
}
@Test // The find operator is: =~
void testFindOperator() {
assert "cheese" =~ "cheese"
def regex = "cheese"
def string = "cheese"
assert string =~ regex
def i = 0
def m = "cheesecheese" =~ "cheese"
assert m instanceof Matcher
while (m.find()) {
i = i + 1
}
assert i == 2
i = 0
m = "cheesecheese" =~ "e+"
while (m.find()) {
i = i + 1
}
assert i == 4
m.reset()
m.find()
m.find()
m.find()
assert m.group() == "ee"
}
@Test // From the javadoc of the getAt() method
void testMatcherWithIntIndex() {
def p = /ab[d|f]/
def m = "abcabdabeabf" =~ p
assert 2 == m.count
assert 2 == m.size() // synonym for m.getCount()
assert !m.hasGroup()
assert 0 == m.groupCount()
def matches = ["abd", "abf"]
for (i in 0..<m.count) {
assert m[i] == matches[i]
}
p = /(?:ab([c|d|e|f]))/
m = "abcabdabeabf" =~ p
assert 4 == m.count
assert m.hasGroup()
assert 1 == m.groupCount()
matches = [["abc", "c"], ["abd", "d"], ["abe", "e"], ["abf", "f"]]
for (i in 0..<m.count) {
assert m[i] == matches[i]
}
m = "abcabdabeabfabxyzabx" =~ /(?:ab([d|x-z]+))/
assert 3 == m.count
assert m.hasGroup()
assert 1 == m.groupCount()
matches = [["abd", "d"], ["abxyz", "xyz"], ["abx", "x"]]
for (i in 0..<m.count) {
assert m[i] == matches[i]
}
}
@Test
void testMatcherWithIndexAndRanges() {
def string = "cheesecheese"
def matcher = string =~ "e+"
assert "ee" == matcher[2]
assert ["ee", "e"] == matcher[2..3]
assert ["ee", "ee"] == matcher[0, 2]
assert ["ee", "e", "ee"] == matcher[0, 1..2]
matcher = "cheese please" =~ /([^e]+)e+/
assert ["se", "s"] == matcher[1]
assert [["se", "s"], [" ple", " pl"]] == matcher[1, 2]
assert [["se", "s"], [" ple", " pl"]] == matcher[1..2]
assert [["chee", "ch"], [" ple", " pl"], ["ase", "as"]] == matcher[0, 2..3]
matcher = "cheese please" =~ /([^e]+)e+/
shouldFail { matcher[0, [1, 2]] }
}
@Test
void testMatcherIterator() {
def matcher = "cheesecheese" =~ "e+"
def iter = matcher.iterator()
assert iter instanceof Iterator
assert iter.hasNext()
assert "ee" == iter.next()
assert iter.hasNext()
assert "e" == iter.next()
assert iter.hasNext()
assert "ee" == iter.next()
assert iter.hasNext()
assert "e" == iter.next()
assert !iter.hasNext()
shouldFail(NoSuchElementException.class, { iter.next() })
matcher = "cheese please" =~ /([^e]+)e+/
iter = matcher.iterator()
assert iter instanceof Iterator
assert iter.hasNext()
assert ["chee", "ch"] == iter.next()
assert iter.hasNext()
assert ["se", "s"] == iter.next()
assert iter.hasNext()
assert [" ple", " pl"] == iter.next()
assert iter.hasNext()
assert ["ase", "as"] == iter.next()
assert !iter.hasNext()
shouldFail(NoSuchElementException.class, { iter.next() })
// collect() uses iterator
matcher = "cheesecheese" =~ "e+"
assert ["ee", "e", "ee", "e"] == matcher.collect { it }
matcher = "cheese please" =~ /([^e]+)e+/
assert [["chee", "ch"], ["se", "s"], [" ple", " pl"], ["ase", "as"]] == matcher.collect { it }
}
@Test
void testMatcherEach() {
def count = 0
def result = []
("cheesecheese" =~ "cheese").each { value -> result += value; count = count + 1 }
assert count == 2
assert result == ['cheese', 'cheese']
count = 0
result = []
("cheesecheese" =~ "ee+").each { result += it; count = count + 1 }
assert count == 2
assert result == ['ee', 'ee']
def matcher = "cheese please" =~ /([^e]+)e+/
def resultAll = []
def resultGroup = []
matcher.each { a, g ->
resultAll << a
resultGroup << g
}
assert ["chee", "se", " ple", "ase"] == resultAll
assert ["ch", "s", " pl", "as"] == resultGroup
matcher = "cheese please" =~ /([^e]+)e+/
result = []
matcher.each { result << it }
assert [["chee", "ch"], ["se", "s"], [" ple", " pl"], ["ase", "as"]] == result
}
@Test // Check consistency between each and collect
void testMatcherEachVsCollect() {
def matcher = "cheese cheese" =~ "e+"
def result = []
matcher.each { result << it }
matcher.reset()
assert result == matcher.collect { it }
matcher = "cheese please" =~ /([^e]+)e+/
result = []
matcher.each { result << it }
matcher.reset()
assert result == matcher.collect { it }
matcher = "cheese please" =~ /([^e]+)e+/
result = []
matcher.each { a, g -> result << a }
matcher.reset()
assert result == matcher.collect { a, g -> a }
matcher = "cheese please" =~ /([^e]+)e+/
result = []
matcher.each { a, g -> result << g }
matcher.reset()
assert result == matcher.collect { a, g -> g }
}
@Test
void testSimplePattern() {
def pattern = ~"foo"
assert pattern instanceof Pattern
assert pattern.matcher("foo").matches()
assert !pattern.matcher("bar").matches()
}
@Test
void testMultiLinePattern() {
def pattern = ~"""foo"""
assert pattern instanceof Pattern
assert pattern.matcher("foo").matches()
assert !pattern.matcher("bar").matches()
}
@Test
void testPatternInAssertion() {
assert "foofoofoo" =~ ~"foo"
}
@Test
void testMatcherWithReplace() {
def matcher = "cheese-cheese" =~ "cheese"
def answer = matcher.replaceAll("edam")
assert answer == 'edam-edam'
def cheese = ("cheese cheese!" =~ "cheese").replaceFirst("nice")
assert cheese == "nice cheese!"
}
@Test
void testGetLastMatcher() {
assert "cheese" ==~ "cheese"
assert Matcher.getLastMatcher().matches()
switch ("cheesefoo") {
case ~"cheesecheese":
assert false;
break;
case ~"(cheese)(foo)":
def m = Matcher.getLastMatcher();
assert m.group(0) == "cheesefoo"
assert m.group(1) == "cheese"
assert m.group(2) == "foo"
assert m.groupCount() == 2
break;
default:
assert false
}
}
@Test
void testRyhmeMatchGina() {
def myFairStringy = 'The rain in Spain stays mainly in the plain!'
// words, that end with 'ain': \b\w*ain\b
def BOUNDS = /\b/
def rhyme = /$BOUNDS\w*ain$BOUNDS/
def found = ''
myFairStringy.eachMatch(rhyme) { match ->
found += match + ' '
}
assert found == 'rain Spain plain '
// a second way that is equivalent
found = ''
(myFairStringy =~ rhyme).each { match ->
found += match + ' '
}
assert found == 'rain Spain plain '
}
@Test
void testEachMatchWithPattern() {
def compiledPattern = ~/.at/
def result = []
"The cat sat on the hat".eachMatch(compiledPattern) { result << it }
assert "cat sat hat" == result.join(" ")
}
@Test
void testPatternVersionsOfStringRegexMethods() {
def compiledPattern = ~/.at/
def s = "The cat sat on the hat"
assert "bat".matches(compiledPattern)
assert s.replaceFirst(compiledPattern, 'x') == "The x sat on the hat"
assert s.replaceAll(compiledPattern, 'x') == "The x x on the x"
}
@Test
void testFindOperatorCollect() {
def m = 'coffee' =~ /ee/
def result = ''
m.each { result += it }
assert result == 'ee'
result = ''
m.each { result += it }
assert result == 'ee'
m.reset()
result = ''
m.each { result += it }
assert result == 'ee'
m = 'reek coffee' =~ /ee/
assert m.collect { it }.join(',') == 'ee,ee'
assert m.collect { it }.join(',') == 'ee,ee'
m.reset()
assert m.collect { it }.join(',') == 'ee,ee'
}
@Test
void testIteration() {
def string = 'a:1 b:2 c:3'
def result = []
def letters = ''
def numbers = ''
string.eachMatch(/([a-z]):(\d)/) { full, group1, group2 ->
result += full
letters += group1
numbers += group2
}
assert result == ['a:1', 'b:2', 'c:3']
assert letters == 'abc'
assert numbers == '123'
result = []
letters = ''
numbers = ''
def matcher = string =~ /([a-z]):(\d)/
matcher.each { match ->
result += match[0]
letters += match[1]
numbers += match[2]
}
assert result == ['a:1', 'b:2', 'c:3']
assert letters == 'abc'
assert numbers == '123'
}
@Test
void testFirst() {
assert "cheesecheese" =~ "cheese"
assert "cheesecheese" =~ /cheese/
assert "cheese" == /cheese/ /*they are both string syntaxes*/
}
@Test
void testSecond() {
// Let's create a regex Pattern
def pattern = ~/foo/
assert pattern instanceof Pattern
assert pattern.matcher("foo").matches()
}
@Test
void testThird() {
// Let's create a Matcher
def matcher = "cheesecheese" =~ /cheese/
assert matcher instanceof Matcher
def answer = matcher.replaceAll("edam")
assert answer == "edamedam"
}
@Test
void testFourth() {
// Let's do some replacement
def cheese = ("cheesecheese" =~ /cheese/).replaceFirst("nice")
assert cheese == "nicecheese"
}
@Test
void testFifth() {
def matcher = "\$abc." =~ "\\\$(.*)\\."
matcher.matches(); // must be invoked
assert matcher.group(1) == "abc" // is one, not zero
assert matcher[0] == ["\$abc.", "abc"]
assert matcher[0][1] == "abc"
}
@Test
void testSixth() {
def matcher = "\$abc." =~ /\$(.*)\./ // no need to double-escape!
assert "\\\$(.*)\\." == /\$(.*)\./
matcher.matches(); // must be invoked
assert matcher.group(1) == "abc" // is one, not zero
assert matcher[0] == ["\$abc.", "abc"]
assert matcher[0][1] == "abc"
}
@Test
void testReplaceWithClosure() {
assert '1-FISH, two fish' == "one fish, two fish".replaceFirst(~/([a-z]{3})\s([a-z]{4})/) {
[one: 1, two: 2][it[1]] + '-' + it[2].toUpperCase()
}
assert '1-FISH, 2-FISH' == "one fish, two fish".replaceAll(~/([a-z]{3})\s([a-z]{4})/) {
[one: 1, two: 2][it[1]] + '-' + it[2].toUpperCase()
}
}
@Test
void testNoGroupMatcherAndGet() {
def p = /ab[d|f]/
def m = "abcabdabeabf" =~ p
def result = []
for (i in 0..<m.count) {
assert m.groupCount() == 0 // no groups
result += "$i:${m[i]}"
}
assert result == ['0:abd', '1:abf']
}
@Test
void testFindWithOneGroupAndGet() {
def p = /(?:ab([c|d|e|f]))/
def m = "abcabdabeabf" =~ p
def result = []
for (i in 0..<m.count) {
assert m.groupCount() == 1
result += "$i:${m[i]}"
}
assert result == ['0:[abc, c]', '1:[abd, d]', '2:[abe, e]', '3:[abf, f]']
}
@Test
void testAnotherOneGroupMatcherAndGet() {
def m = "abcabdabeabfabxyzabx" =~ /(?:ab([d|x-z]+))/
def result = []
m.count.times {
assert m.groupCount() == 1
result += "$it:${m[it]}"
}
assert result == ['0:[abd, d]', '1:[abxyz, xyz]', '2:[abx, x]']
}
void replaceAllHelper(p, c) {
assert 'x12345' == 'x123z45'.replaceAll(p, c)
assert '12345' == '12345z'.replaceAll(p, c)
assert '12345' == 'z12345z'.replaceAll(p, c)
assert '12345' == 'z12z345z'.replaceAll(p, c)
assert '12345' == 'zz12z3zz45z'.replaceAll(p, c)
assert '12345' == 'z12z3zzz45z'.replaceAll(p, c)
assert '12345' == '12345'.replaceAll(p, c)
assert '$1$2345' == 'z$1$2345'.replaceAll(p, c)
assert '1$2345$' == 'z1$2345$'.replaceAll(p, c)
assert '2345\\' == '23z45zz\\'.replaceAll(p, c)
assert 'x1\\2345' == 'x1\\23z4zz5'.replaceAll(p, c)
assert '\\2345' == '\\23z45zzz'.replaceAll(p, c)
assert '\\2345\\' == '\\23z45\\'.replaceAll(p, c)
assert '\\23\\\\45\\' == '\\23\\\\45\\'.replaceAll(p, c)
assert '$\\23\\$\\45\\' == '$\\23\\$\\45\\'.replaceAll(p, c)
}
@Test
void testReplaceAllClosure() {
def p = /([^z]*)(z)/
def c = { all, m, d -> m }
replaceAllHelper(p, c)
}
@Test
void testReplaceAllClosureWithIt() {
def p = /([^z]*)(z)/
def c = { it[1] }
replaceAllHelper(p, c)
}
@Test
void testReplaceAllClosureWithObjectArray() {
def p = /([^z]*)(z)/
def c = { Object[] a -> a[1] }
replaceAllHelper(p, c)
}
@Test
void testFind() {
def p = /.ar/
assert null == 'foo foo baz'.find(p)
assert 'bar' == 'foo bar baz'.find(p)
assert 'car' == 'car'.find(p)
def patternWithGroups = /(.)ar/
assert null == ''.find(patternWithGroups)
assert 'bar' == 'foo bar baz'.find(patternWithGroups)
def compiledPattern = ~/(.)ar/
assert null == ''.find(compiledPattern)
assert 'bar' == 'foo bar baz'.find(compiledPattern)
}
@Test
void testFindClosureNoGroups() {
def p = /.ar/
def c = { match -> return "-$match-" }
assert null == 'foo foo baz'.find(p, c)
assert '-bar-' == 'foo bar baz'.find(p, c)
assert '-car-' == 'car'.find(p, c)
def compiledPattern = ~p
assert null == 'foo foo baz'.find(compiledPattern, c)
assert '-bar-' == 'foo bar baz'.find(compiledPattern, c)
assert '-car-' == 'car'.find(compiledPattern, c)
}
@Test
void testFindClosureWithGroups() {
def AREA_CODE = /\d{3}/
def EXCHANGE = /\d{3}/
def STATION_NUMBER = /\d{4}/
def phone = /($AREA_CODE)-($EXCHANGE)-($STATION_NUMBER)/
def c = { match, areaCode, exchange, stationNumber ->
return "($areaCode) $exchange-$stationNumber"
}
assert null == 'foo'.find(phone) { match, areaCode, exchange, stationNumber -> return match }
assert "612-555-1212" == 'foo 612-555-1212 bar'.find(phone) { match, areaCode, exchange, stationNumber -> return match }
assert "(612) 555-1212" == 'foo 612-555-1212 bar'.find(phone, c)
def compiledPhonePattern = ~phone
assert "(888) 555-1212" == "bar 888-555-1212 foo".find(compiledPhonePattern, c)
def closureSingleVar = { matchArray ->
return "(${matchArray[1]}) ${matchArray[2]}-${matchArray[3]}"
}
assert "(888) 555-1212" == "bar 888-555-1212 foo".find(compiledPhonePattern, closureSingleVar)
}
@Test
void testFindAll() {
def p = /.at/
def compiledPattern = ~p
assert [] == "".findAll(p)
assert [] == "".findAll(compiledPattern)
def orig = "The cat sat on the hat"
assert ["cat", "sat", "hat"] == orig.findAll(p)
assert ["cat", "sat", "hat"] == orig.findAll(compiledPattern)
assert ["+cat", "+sat", "+hat"] == orig.findAll(p) { "+$it" }
assert ["+cat", "+sat", "+hat"] == orig.findAll(compiledPattern) { "+$it" }
}
@Test
void testFindAllWithGroups() {
def p = /(.)a(.)/
def compiledPattern = ~p
def orig = "The cat sat on the hat"
assert ["cat", "sat", "hat"] == orig.findAll(p)
assert ["cat", "sat", "hat"] == orig.findAll(compiledPattern)
def c = { match, firstLetter, lastLetter -> return "$firstLetter+$match+$lastLetter" }
assert ["c+cat+t", "s+sat+t", "h+hat+t"] == orig.findAll(p, c)
assert ["c+cat+t", "s+sat+t", "h+hat+t"] == orig.findAll(compiledPattern, c)
def closureSingleVar = { matchArray -> return "${matchArray[1]}+${matchArray[0]}+${matchArray[2]}" }
assert ["c+cat+t", "s+sat+t", "h+hat+t"] == orig.findAll(p, closureSingleVar)
assert ["c+cat+t", "s+sat+t", "h+hat+t"] == orig.findAll(compiledPattern, closureSingleVar)
}
@Test
void testMatchesPartially() {
def pattern = /\w+@\w+\.\w{2,}/
def useCases = [
"glaforge@gmail.com": true,
"glaforge" : true,
"!!!!" : false,
"glaforge@" : true,
"glaforge@@" : false
]
useCases.each { String email, boolean bool ->
def matcher = email =~ pattern
assert matcher.matchesPartially() == bool
}
}
}