DefaultInterpolatorTest.java
/*
* 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 org.apache.maven.impl.model;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.UnaryOperator;
import org.apache.maven.api.services.InterpolatorException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
class DefaultInterpolatorTest {
@Test
void testBasicSubstitution() {
Map<String, String> props = new HashMap<>();
props.put("key0", "value0");
props.put("key1", "${value1}");
props.put("key2", "${value2}");
performSubstitution(props, Map.of("value1", "sub_value1")::get);
assertEquals("value0", props.get("key0"));
assertEquals("sub_value1", props.get("key1"));
assertEquals("", props.get("key2"));
}
@Test
void testBasicSubstitutionWithContext() {
HashMap<String, String> props = new HashMap<>();
props.put("key0", "value0");
props.put("key1", "${value1}");
performSubstitution(props, Map.of("value1", "sub_value1")::get);
assertEquals("value0", props.get("key0"));
assertEquals("sub_value1", props.get("key1"));
}
@Test
void testSubstitutionFailures() {
assertEquals("a}", substVars("a}", "b"));
assertEquals("${a", substVars("${a", "b"));
}
@Test
void testEmptyVariable() {
assertEquals("", substVars("${}", "b"));
}
@Test
void testInnerSubst() {
assertEquals("c", substVars("${${a}}", "z", Map.of("a", "b", "b", "c")));
}
@Test
void testSubstLoop() {
assertThrows(
InterpolatorException.class,
() -> substVars("${a}", "a"),
"Expected substVars() to throw an InterpolatorException, but it didn't");
}
@Test
void testLoopEmpty() {
assertEquals("${a}", substVars("${a}", null, null, null, false));
}
@Test
void testLoopEmpty2() {
assertEquals("${a}", substVars("${a}", null, null, null, false));
}
@Test
void testSubstitutionEscape() {
assertEquals("${a}", substVars("$\\{a${#}\\}", "b"));
assertEquals("${a}", substVars("$\\{a\\}${#}", "b"));
assertEquals("${a}", substVars("$\\{a\\}", "b"));
assertEquals("\\\\", substVars("\\\\", "b"));
}
@Test
void testSubstitutionOrder() {
LinkedHashMap<String, String> map1 = new LinkedHashMap<>();
map1.put("a", "$\\\\{var}");
map1.put("abc", "${ab}c");
map1.put("ab", "${a}b");
performSubstitution(map1);
LinkedHashMap<String, String> map2 = new LinkedHashMap<>();
map2.put("a", "$\\\\{var}");
map2.put("ab", "${a}b");
map2.put("abc", "${ab}c");
performSubstitution(map2);
assertEquals(map1, map2);
}
@Test
void testMultipleEscapes() {
LinkedHashMap<String, String> map1 = new LinkedHashMap<>();
map1.put("a", "$\\\\{var}");
map1.put("abc", "${ab}c");
map1.put("ab", "${a}b");
performSubstitution(map1);
assertEquals("$\\{var}", map1.get("a"));
assertEquals("$\\{var}b", map1.get("ab"));
assertEquals("$\\{var}bc", map1.get("abc"));
}
@Test
void testPreserveUnresolved() {
Map<String, String> props = new HashMap<>();
props.put("a", "${b}");
assertEquals("", substVars("${b}", "a", props, null, true));
assertEquals("${b}", substVars("${b}", "a", props, null, false));
props.put("b", "c");
assertEquals("c", substVars("${b}", "a", props, null, true));
assertEquals("c", substVars("${b}", "a", props, null, false));
props.put("c", "${d}${d}");
assertEquals("${d}${d}", substVars("${d}${d}", "c", props, null, false));
}
@Test
void testExpansion() {
Map<String, String> props = new LinkedHashMap<>();
props.put("a", "foo");
props.put("b", "");
props.put("a_cm", "${a:-bar}");
props.put("b_cm", "${b:-bar}");
props.put("c_cm", "${c:-bar}");
props.put("a_cp", "${a:+bar}");
props.put("b_cp", "${b:+bar}");
props.put("c_cp", "${c:+bar}");
performSubstitution(props);
assertEquals("foo", props.get("a_cm"));
assertEquals("bar", props.get("b_cm"));
assertEquals("bar", props.get("c_cm"));
assertEquals("bar", props.get("a_cp"));
assertEquals("", props.get("b_cp"));
assertEquals("", props.get("c_cp"));
}
@Test
void testTernary() {
Map<String, String> props;
props = new LinkedHashMap<>();
props.put("foo", "-FOO");
props.put("bar", "-BAR");
props.put("version", "1.0${release:+${foo}:-${bar}}");
performSubstitution(props);
assertEquals("1.0-BAR", props.get("version"));
props = new LinkedHashMap<>();
props.put("release", "true");
props.put("foo", "-FOO");
props.put("bar", "-BAR");
props.put("version", "1.0${release:+${foo}:-${bar}}");
performSubstitution(props);
assertEquals("1.0-FOO", props.get("version"));
props = new LinkedHashMap<>();
props.put("foo", "");
props.put("bar", "-BAR");
props.put("version", "1.0${release:+${foo}:-${bar}}");
performSubstitution(props);
assertEquals("1.0-BAR", props.get("version"));
props = new LinkedHashMap<>();
props.put("release", "true");
props.put("foo", "");
props.put("bar", "-BAR");
props.put("version", "1.0${release:+${foo}:-${bar}}");
performSubstitution(props);
assertEquals("1.0", props.get("version"));
props = new LinkedHashMap<>();
props.put("version", "1.0${release:+:--BAR}");
performSubstitution(props);
assertEquals("1.0-BAR", props.get("version"));
props = new LinkedHashMap<>();
props.put("release", "true");
props.put("version", "1.0${release:+:--BAR}");
performSubstitution(props);
assertEquals("1.0", props.get("version"));
}
@Test
void testXdg() {
Map<String, String> props;
props = new LinkedHashMap<>();
props.put("user.home", "/Users/gnodet");
props.put(
"maven.user.config",
"${env.MAVEN_XDG:+${env.XDG_CONFIG_HOME:-${user.home}/.config/maven}:-${user.home}/.m2}");
performSubstitution(props);
assertEquals("/Users/gnodet/.m2", props.get("maven.user.config"));
props = new LinkedHashMap<>();
props.put("user.home", "/Users/gnodet");
props.put(
"maven.user.config",
"${env.MAVEN_XDG:+${env.XDG_CONFIG_HOME:-${user.home}/.config/maven}:-${user.home}/.m2}");
props.put("env.MAVEN_XDG", "true");
performSubstitution(props);
assertEquals("/Users/gnodet/.config/maven", props.get("maven.user.config"));
props = new LinkedHashMap<>();
props.put("user.home", "/Users/gnodet");
props.put(
"maven.user.config",
"${env.MAVEN_XDG:+${env.XDG_CONFIG_HOME:-${user.home}/.config/maven}:-${user.home}/.m2}");
props.put("env.MAVEN_XDG", "true");
props.put("env.XDG_CONFIG_HOME", "/Users/gnodet/.xdg/maven");
performSubstitution(props);
assertEquals("/Users/gnodet/.xdg/maven", props.get("maven.user.config"));
}
private void performSubstitution(Map<String, String> props) {
performSubstitution(props, null);
}
private void performSubstitution(Map<String, String> props, UnaryOperator<String> callback) {
new DefaultInterpolator().performSubstitution(props, callback);
}
private String substVars(
String val,
String currentKey,
Map<String, String> configProps,
UnaryOperator<String> callback,
boolean defaultsToEmptyString) {
return DefaultInterpolator.substVars(val, currentKey, null, configProps, callback, null, defaultsToEmptyString);
}
private String substVars(String val, String currentKey) {
return DefaultInterpolator.substVars(val, currentKey, null, null, null, null, true);
}
private String substVars(String val, String currentKey, Map<String, String> configProps) {
return new DefaultInterpolator().substVars(val, currentKey, null, configProps);
}
}