blob: d334db98a823b0c50af5ea206df3db8ac9ac3d31 [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 groovy.test.GroovyTestCase
class GStringTest extends GroovyTestCase {
void check(template, teststr) {
assert template instanceof GString
def count = template.getValueCount()
assert count == 1
assert template.getValue(0) == "Bob"
def string = template.toString()
assert string == teststr
}
void testEmptyGString() {
def foo = 'Foo'
def bar = 'Bar'
def g = GString.EMPTY + "$foo".toString() + "$bar"
assert g instanceof GString
assert g.values == ['Bar']
assert 'FooBar' == g
}
void testWithOneVariable() {
def name = "Bob"
def teststr = "hello Bob how are you?"
check("hello $name how are you?", teststr)
check("hello ${name} how are you?", teststr)
check("hello ${(name + ' ').trim()} how are you?", teststr)
check(/hello $name how are you?/, teststr)
check(/hello ${name} how are you?/, teststr)
check(/hello ${(name + ' ').trim()} how are you?/, teststr)
}
void testWithVariableAtEnd() {
def name = "Bob"
def teststr = "hello Bob"
check("hello $name", teststr)
check("hello ${name}", teststr)
check(/hello $name/, teststr)
check(/hello ${name}/, teststr)
}
void testWithVariableAtBeginning() {
def name = "Bob"
def teststr = "Bob hey"
check("$name hey", teststr)
check("${name} hey", teststr)
name = ""
check("${name += "Bob"; name} hey", teststr)
assert name == "Bob"
check(/$name hey/, teststr)
check(/${name} hey/, teststr)
name = ""
check(/${name += "Bob"; name} hey/, teststr)
}
void testWithJustVariable() {
def teststr
def name = teststr = "Bob"
check("$name", teststr)
check("${name}", teststr)
check("${assert name == "Bob"; name}", teststr)
// Put punctuation after the variable name:
check("$name.", "Bob.")
check("$name...", "Bob...")
check("$name?", "Bob?")
check(/$name/, teststr)
check(/${name}/, teststr)
check(/${assert name == "Bob"; name}/, teststr)
// Put punctuation after the variable name:
check(/$name./, "Bob.")
check(/$name.../, "Bob...")
check(/$name?/, "Bob?")
check(/$name\?/, "Bob\\?")
check(/$name$/, "Bob\$")
def guy = [name: name]
check("${guy.name}", "Bob")
check("$guy.name", "Bob")
check("$guy.name.", "Bob.")
check("$guy.name...", "Bob...")
check("$guy.name?", "Bob?")
check(/$guy.name/, "Bob")
check(/$guy.name./, "Bob.")
check(/$guy.name.../, "Bob...")
check(/$guy.name?/, "Bob?")
check(/$guy.name\?/, "Bob\\?")
check(/$guy.name$/, "Bob\$")
}
void testWithTwoVariables() {
def name = "Bob"
def template = "${name}${name}"
def string = template.toString()
assert string == "BobBob"
}
void testWithTwoVariablesWithSpace() {
def name = "Bob"
def template = "${name} ${name}"
def string = template.toString()
assert string == "Bob Bob"
}
void testAppendString() {
def a = "dog"
def b = "a ${a}"
def c = b + " cat"
assert c.toString() == "a dog cat", c
b += " cat"
assert b.toString() == "a dog cat", b
}
void testAppendGString() {
def a = "dog"
def b = "a ${a}"
b += " cat${a}"
assert b.toString() == "a dog catdog", b
}
void testReturnString() {
def value = dummyMethod()
assert value == "Hello Gromit!"
}
String dummyMethod() {
def name = "Gromit"
return "Hello ${name}!"
}
void testCoerce() {
def enc = "US-ASCII"
def value = "test".getBytes("${enc}")
assert value == [116, 101, 115, 116]
}
void testGroovy441() {
def arg = "test"
def content = "${arg} ="
if (arg != "something") {
content += "?"
}
content += "= ${arg}."
assert content == "test =?= test."
}
void testTwoStringsInMiddle() {
def a = "---"
def b = "${a} :"
b += "<<"
b += ">>"
b += ": ${a}"
assert b == "--- :<<>>: ---"
}
void testAlternatingGStrings() {
def a = "---"
def b = "${a} :"
b += "<<"
b += " [[${a}]] "
b += ">>"
b += ": ${a}"
assert b == "--- :<< [[---]] >>: ---"
}
// Test case for GROOVY-599
void testGStringInStaticMethod() {
int value = 2
String str = "1${value}3"
int result = Integer.parseInt(str)
assert result == 123
result = Integer.parseInt("1${value}3")
assert result == 123
}
// Test case for GROOVY-2275
void testGetAtWithRange() {
def number = 1234567
def numberString = "${number}"
def realString = "1234567"
assert numberString[0..-1] == '1234567'
assert realString[0..-1] == '1234567'
}
void testEmbeddedClosures() {
def c1 = {-> "hello"}
def c2 = {out -> out << "world"}
def c3 = {a, b -> b << a}
def c4 = c3.curry(5)
def g1 = "${-> "hello"} ${out -> out << "world"}"
def g2 = "$c1 $c2"
def g3 = "${-> c1} ${-> c2}"
def g4 = "$c4"
def g5 = "$c3"
def w = new StringWriter()
w << g1
assertEquals(w.buffer.toString(), "hello world")
assertEquals(g1.toString(), "hello world")
w = new StringWriter()
w << g2
assertEquals(w.buffer.toString(), "hello world")
assertEquals(g2.toString(), "hello world")
w = new StringWriter()
w << g3
assert w.buffer.toString().contains("closure")
assert g3.toString().contains("closure")
w = new StringWriter()
w << g4
assertEquals(w.buffer.toString(), "5")
assertEquals(g4.toString(), "5")
try {
w << g5
fail("should throw a GroovyRuntimeException")
} catch (GroovyRuntimeException e) {
}
try {
g5.toString()
fail("should throw a GroovyRuntimeException")
} catch (GroovyRuntimeException e) {
}
}
/**
* Tests comparing two strings which have the same string representation but
* only one of which uses a template. This is a test for GROOVY-626.
*/
void testEqualsTemplateToLiteral() {
def template = "${2}"
def literal = "2"
// succeeds
assertTrue("template == literal false", template == literal)
assertTrue("literal == template false", literal == template)
// these fail
assertFalse("literal not equal to template", literal.equals(template))
assertFalse("template not equal to literal", template.equals(literal))
assertTrue("hash codes not equal", literal.hashCode() != template.hashCode())
}
/**
* Tests getting a character by index where the index is a reference instead of a constant.
* This is a test for GROOVY-1139.
*/
void testCharAtWithIntegerReference() {
def literal = "0123456789";
def template = "${literal}";
def i = 0
assertEquals("wrong character at position 0", '0', literal[i]);
assertEquals("wrong character at position 0", '0', template[i]);
i = 5
assertEquals("wrong character at position 5", '5', literal[i]);
assertEquals("wrong character at position 5", '5', template[i]);
i = 9
assertEquals("wrong character at position 9", '9', literal[i]);
assertEquals("wrong character at position 9", '9', template[i]);
}
/**
* Tests getting a character by index, counting from the beginning of the string.
*/
void testCharAtFromStart() {
def literal = "0123456789";
def template = "${literal}";
assertEquals("wrong character at position 0", '0', literal[0]);
assertEquals("wrong character at position 5", '5', literal[5]);
assertEquals("wrong character at position 9", '9', literal[9]);
assertEquals("wrong character at position 0", '0', template[0]);
assertEquals("wrong character at position 5", '5', template[5]);
assertEquals("wrong character at position 9", '9', template[9]);
}
/**
* Tests getting a character by index, counting from the end of the string.
*/
void testCharAtFromEnd() {
def literal = "0123456789";
def template = "${literal}";
assertEquals("wrong character at position -1", '9', literal[-1]);
assertEquals("wrong character at position -5", '5', literal[-5]);
assertEquals("wrong character at position -10", '0', literal[-10]);
assertEquals("wrong character at position -1", '9', template[-1]);
assertEquals("wrong character at position -5", '5', template[-5]);
assertEquals("wrong character at position -10", '0', template[-10]);
}
/**
* Tests extracting a range which starts at position zero and ends before the
* end of the string.
*/
private void doTestExtractRangeStartToBeforeEnd(string) {
// inclusive
assertEquals("string[0..-3]", "01234567", string[0..-3]);
// exclusive
assertEquals("string[0..<-3]", "0123456", string[0..<-3]);
}
/**
* Tests extracting a range which starts before the end of the string and ends at 0.
* For an inclusive range, this reverses the string. For an exclusive range, this
* counts from the end to the last character.
*/
void doTestExtractRangeBeforeEndToStart(string) {
// inclusive
assertEquals("string[-3..0]", "76543210", string[-3..0]);
// exclusive
assertEquals("string[-3..<0]", "7654321", string[-3..<0]);
}
/**
* Calls <code>doTestExtractRangeStartToBeforeEnd</code> with a non-template string.
*
* GROOVY-781
*/
void testExtractRangeStartToBeforeEndLiteral() {
def literal = "0123456789";
doTestExtractRangeStartToBeforeEnd(literal);
}
/**
* Calls <code>doTestExtractRangeStartToBeforeEnd</code> with a template string.
*
* GROOVY-781
*/
void testExtractRangeStartToBeforeEndTemplate() {
def literal = "0123456789";
def template = "${literal}";
doTestExtractRangeStartToBeforeEnd(template);
}
/**
* Calls <code>doTestExtractRangeBeforeEndToStart</code> with a non-template string.
*
* GROOVY-781
*/
void testExtractRangeBeforeEndToStartLiteral() {
def literal = "0123456789";
doTestExtractRangeBeforeEndToStart(literal);
}
/**
* Calls <code>doTestExtractRangeBeforeEndToStart</code> with a template string.
*
* GROOVY-781
*/
void testExtractRangeBeforeEndToStartTemplate() {
def literal = "0123456789";
def template = "${literal}";
doTestExtractRangeBeforeEndToStart(template);
}
/**
* Tests replacing a value in a string with an undefined variable.
*/
void testReplaceValueWithUndefinedVariable() {
try {
def str = "replace <${undefined}>"
fail("undefined value not detected");
}
catch (MissingPropertyException e) {
}
}
/**
* Tests replacing a value with a null expression.
*/
void testReplaceValueWithNullExpression() {
def str = "replace <${null}>";
assertEquals(str, 'replace <null>');
}
/**
* Tests replacing a value with a compound expression.
*/
void testReplaceValueWithCompoundExpression() {
def i = 1, j = 2
def str = "replace <${i; j}>"
assertEquals('value replaced', 'replace <2>', str)
}
/**
* Tests incrementing the variable being substituted after creating the string. This
* shouldn't have any effect because the string holds a reference to the original value.
*/
void testIncrementAfterCreatingString() {
def i = 1
def str = "replace <${i}> <${i * 2}>"
assertEquals("value ok", "replace <1> <2>", str)
i++
assertEquals("value ok", "replace <1> <2>", str)
}
/**
* Tests evaluating a closure embedded in a string.
*/
void testClosureInString() {
def i = 1
def closure = {i};
def str = "<${closure()}>"
assertEquals('closure replacement ok', '<1>', str)
// this has no effect because the closure is only evaluated once when the string
// is created
i++
assertEquals('closure replacement ok', '<1>', str)
}
/**
* Tests embedding a mutable object in a string.
*/
void testEmbedMutableObject() {
def buffer = new StringBuffer("value")
def stringValue = "value";
def str = "buffer: <${buffer}>"
assertEquals("buffer: <value>", str)
}
/**
* Tests modifying a string embedded in another string, which should have no effect.
*/
void testAppendToEmbeddedStringValue() {
def stringValue = "value";
def str = "string: <${stringValue}>"
assertEquals("string: <value>", str)
// this has no effect because the string contains a reference stringValue
// and += for strings creates a new string instead of modifying the existing value.
stringValue += " more"
assertEquals("string: <value>", str)
}
/**
* Tests including a map in a string
*/
void testMapInString() {
def map = ["key": 1];
def str = "map.key: <${map.key}>; map: <${map}>";
assertEquals("map replacement ok", 'map.key: <1>; map: <[key:1]>', str)
map.key++;
// The map shows the effects of the change because the string holds a reference
// to the mutable map. map.key doesn't show the effect of the change because
// in this slot the string holds a reference to the original value and
// map.kep++ created a new value which was stored in the map.
assertEquals("map replacement ok", 'map.key: <1>; map: <[key:2]>', str)
}
/**
* Tests including a string in itself recursively.
*/
void testRecursiveReplacement() {
def str = "1";
str = "<${str}>";
assertEquals("recursive string replaced", '<1>', str);
}
/**
* Void method
*/
void doNothing()
{
}
/**
* Tests replacing a value with a void statement.
*/
void testReplaceValueWithVoidStatement() {
def str = "replace <${doNothing()}>"
assertEquals('value replaced', 'replace <null>', str)
}
/**
* Tests replacing a value with an empty statement.
*/
void testReplaceValueWithEmptyStatement() {
def str = "replace <${;}>"
assertEquals('value replaced', 'replace <null>', str)
}
/**
* Tests replacing a value with an empty expression.
*/
void testReplaceValueWithEmptyExpression() {
assertEquals('replace <null>', "replace <${}>")
}
/**
* Tests GString concatenation. GROOVY-2848
*/
void testGStringConcatenationAddsNoNewValues() {
def x = "dog"
def y = "woof-woof"
def gs1 = "the ${x} says "
assert gs1.getValues() == ["dog"]
assert gs1.toString() == "the dog says "
gs1 = gs1.plus(" ${y} ")
assert gs1.getValues() == ["dog", "woof-woof"]
assert gs1.toString() == "the dog says woof-woof "
gs1 = gs1.plus(" not the cat")
assert gs1.getValues() == ["dog", "woof-woof"]
assert gs1.toString() == "the dog says woof-woof not the cat"
}
/**
* Tests GString splitting. GROOVY-3359
*/
void testGStringSplitting() {
def gs = "The quick brown ${'xof'.reverse()}"
assert gs.split() == ['The', 'quick', 'brown', 'fox'] as String[]
assert gs.split('o') == ['The quick br', 'wn f', 'x'] as String[]
}
def foo(String s) {1}
void testGStringArgumentForStringParameter() {
def a = 1
def b = "$a"
assert foo(b) == 1
}
/**
* GROOVY-5761 - getBytes for GString
*/
public void testGetBytes() {
String string = 'Hello world'
String world = 'world'
GString gstring = "Hello ${world}"
assert gstring.bytes == string.bytes
assert gstring.getBytes('UTF-8') == string.getBytes('UTF-8')
}
/**
* GROOVY-7377: Interpolated variable followed by asterisk in slashy-string causes compiler error
*/
void testSlashyStringWithInterpolatedVariableFollowedByAsterisk() {
assert Eval.me('''def foo='bar'; /$foo*baz/''') == 'bar*baz'
assert Eval.me('''def foo='bar'; /${foo}*baz/''') == 'bar*baz'
assert Eval.me('''def foo='bar'; /$foo\u002abaz/''') == 'bar*baz'
assert Eval.me('''def foo='bar'; /${foo}\u002abaz/''') == 'bar*baz'
}
}