mirror of
https://github.com/lightbend/config.git
synced 2025-03-22 23:30:27 +08:00
Merge pull request #1 from twitter-forks/substitutions-in-includes
Substitutions in includes
This commit is contained in:
commit
48623a5c44
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
.cache
|
.cache
|
||||||
|
.DS_Store
|
||||||
.settings
|
.settings
|
||||||
.idea
|
.idea
|
||||||
.idea_modules
|
.idea_modules
|
||||||
|
@ -56,4 +56,4 @@ welcome!):
|
|||||||
release)
|
release)
|
||||||
16. push the "vX.Y.Z" tag
|
16. push the "vX.Y.Z" tag
|
||||||
17. announce release, possibly wait for maven central to sync
|
17. announce release, possibly wait for maven central to sync
|
||||||
first
|
first
|
13
HOCON.md
13
HOCON.md
@ -1025,6 +1025,19 @@ Then the `${x}` in "foo.conf", which has been fixed up to
|
|||||||
`${a.x}`, would evaluate to `42` rather than to `10`.
|
`${a.x}`, would evaluate to `42` rather than to `10`.
|
||||||
Substitution happens _after_ parsing the whole configuration.
|
Substitution happens _after_ parsing the whole configuration.
|
||||||
|
|
||||||
|
Includes inside of lists use the list index as a path segment when
|
||||||
|
fixing up substitutions.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{
|
||||||
|
a : [ { include "foo.conf" } ]
|
||||||
|
a.0.x : 42
|
||||||
|
}
|
||||||
|
|
||||||
|
Since the `${x}` in "foo.conf" would be fixed up to `${a.0.x}' we
|
||||||
|
get our expected value of 42.
|
||||||
|
|
||||||
However, there are plenty of cases where the included file might
|
However, there are plenty of cases where the included file might
|
||||||
intend to refer to the application's root config. For example, to
|
intend to refer to the application's root config. For example, to
|
||||||
get a value from a system property or from the reference
|
get a value from a system property or from the reference
|
||||||
|
2
NEWS.md
2
NEWS.md
@ -382,4 +382,4 @@ Thank you to contributors with commits since v1.2.1 tag:
|
|||||||
`${foo}` syntax) without getting confused; just about anything
|
`${foo}` syntax) without getting confused; just about anything
|
||||||
that makes conceptual sense should now work. Only inherently
|
that makes conceptual sense should now work. Only inherently
|
||||||
circular config files should fail.
|
circular config files should fail.
|
||||||
- some minor documentation fixes.
|
- some minor documentation fixes.
|
@ -832,4 +832,4 @@ format.
|
|||||||
|
|
||||||
#### Python port
|
#### Python port
|
||||||
|
|
||||||
* pyhocon https://github.com/chimpler/pyhocon
|
* pyhocon https://github.com/chimpler/pyhocon
|
@ -252,4 +252,4 @@ public class ConfigBeanImpl {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -339,4 +339,4 @@ final class ConfigDelayedMerge extends AbstractConfigValue implements Unmergeabl
|
|||||||
sb.append("# ) end of unresolved merge\n");
|
sb.append("# ) end of unresolved merge\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -233,4 +233,4 @@ final public class ConfigImplUtil {
|
|||||||
}
|
}
|
||||||
return nameBuilder.toString();
|
return nameBuilder.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -278,4 +278,4 @@ final class ConfigNodeObject extends ConfigNodeComplexValue {
|
|||||||
Path path = PathParser.parsePathNode(desiredPath, flavor).value();
|
Path path = PathParser.parsePathNode(desiredPath, flavor).value();
|
||||||
return changeValueOnPath(path, null, flavor);
|
return changeValueOnPath(path, null, flavor);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,11 +34,6 @@ final class ConfigParser {
|
|||||||
final private ConfigOrigin baseOrigin;
|
final private ConfigOrigin baseOrigin;
|
||||||
final private LinkedList<Path> pathStack;
|
final private LinkedList<Path> pathStack;
|
||||||
|
|
||||||
// the number of lists we are inside; this is used to detect the "cannot
|
|
||||||
// generate a reference to a list element" problem, and once we fix that
|
|
||||||
// problem we should be able to get rid of this variable.
|
|
||||||
int arrayCount;
|
|
||||||
|
|
||||||
ParseContext(ConfigSyntax flavor, ConfigOrigin origin, ConfigNodeRoot document,
|
ParseContext(ConfigSyntax flavor, ConfigOrigin origin, ConfigNodeRoot document,
|
||||||
FullIncluder includer, ConfigIncludeContext includeContext) {
|
FullIncluder includer, ConfigIncludeContext includeContext) {
|
||||||
lineNumber = 1;
|
lineNumber = 1;
|
||||||
@ -48,7 +43,6 @@ final class ConfigParser {
|
|||||||
this.includer = includer;
|
this.includer = includer;
|
||||||
this.includeContext = includeContext;
|
this.includeContext = includeContext;
|
||||||
this.pathStack = new LinkedList<Path>();
|
this.pathStack = new LinkedList<Path>();
|
||||||
this.arrayCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge a bunch of adjacent values into one
|
// merge a bunch of adjacent values into one
|
||||||
@ -95,8 +89,6 @@ final class ConfigParser {
|
|||||||
private AbstractConfigValue parseValue(AbstractConfigNodeValue n, List<String> comments) {
|
private AbstractConfigValue parseValue(AbstractConfigNodeValue n, List<String> comments) {
|
||||||
AbstractConfigValue v;
|
AbstractConfigValue v;
|
||||||
|
|
||||||
int startingArrayCount = arrayCount;
|
|
||||||
|
|
||||||
if (n instanceof ConfigNodeSimpleValue) {
|
if (n instanceof ConfigNodeSimpleValue) {
|
||||||
v = ((ConfigNodeSimpleValue) n).value();
|
v = ((ConfigNodeSimpleValue) n).value();
|
||||||
} else if (n instanceof ConfigNodeObject) {
|
} else if (n instanceof ConfigNodeObject) {
|
||||||
@ -114,9 +106,6 @@ final class ConfigParser {
|
|||||||
comments.clear();
|
comments.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arrayCount != startingArrayCount)
|
|
||||||
throw new ConfigException.BugOrBroken("Bug in config parser: unbalanced array count");
|
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,14 +176,6 @@ final class ConfigParser {
|
|||||||
throw new ConfigException.BugOrBroken("should not be reached");
|
throw new ConfigException.BugOrBroken("should not be reached");
|
||||||
}
|
}
|
||||||
|
|
||||||
// we really should make this work, but for now throwing an
|
|
||||||
// exception is better than producing an incorrect result.
|
|
||||||
// See https://github.com/typesafehub/config/issues/160
|
|
||||||
if (arrayCount > 0 && obj.resolveStatus() != ResolveStatus.RESOLVED)
|
|
||||||
throw parseError("Due to current limitations of the config parser, when an include statement is nested inside a list value, "
|
|
||||||
+ "${} substitutions inside the included file cannot be resolved correctly. Either move the include outside of the list value or "
|
|
||||||
+ "remove the ${} statements from the included file.");
|
|
||||||
|
|
||||||
if (!pathStack.isEmpty()) {
|
if (!pathStack.isEmpty()) {
|
||||||
Path prefix = fullCurrentPath();
|
Path prefix = fullCurrentPath();
|
||||||
obj = obj.relativized(prefix);
|
obj = obj.relativized(prefix);
|
||||||
@ -240,21 +221,6 @@ final class ConfigParser {
|
|||||||
|
|
||||||
// path must be on-stack while we parse the value
|
// path must be on-stack while we parse the value
|
||||||
pathStack.push(path);
|
pathStack.push(path);
|
||||||
if (((ConfigNodeField) node).separator() == Tokens.PLUS_EQUALS) {
|
|
||||||
// we really should make this work, but for now throwing
|
|
||||||
// an exception is better than producing an incorrect
|
|
||||||
// result. See
|
|
||||||
// https://github.com/typesafehub/config/issues/160
|
|
||||||
if (arrayCount > 0)
|
|
||||||
throw parseError("Due to current limitations of the config parser, += does not work nested inside a list. "
|
|
||||||
+ "+= expands to a ${} substitution and the path in ${} cannot currently refer to list elements. "
|
|
||||||
+ "You might be able to move the += outside of the list and then refer to it from inside the list with ${}.");
|
|
||||||
|
|
||||||
// because we will put it in an array after the fact so
|
|
||||||
// we want this to be incremented during the parseValue
|
|
||||||
// below in order to throw the above exception.
|
|
||||||
arrayCount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractConfigNodeValue valueNode;
|
AbstractConfigNodeValue valueNode;
|
||||||
AbstractConfigValue newValue;
|
AbstractConfigValue newValue;
|
||||||
@ -265,7 +231,6 @@ final class ConfigParser {
|
|||||||
newValue = parseValue(valueNode, comments);
|
newValue = parseValue(valueNode, comments);
|
||||||
|
|
||||||
if (((ConfigNodeField) node).separator() == Tokens.PLUS_EQUALS) {
|
if (((ConfigNodeField) node).separator() == Tokens.PLUS_EQUALS) {
|
||||||
arrayCount -= 1;
|
|
||||||
|
|
||||||
List<AbstractConfigValue> concat = new ArrayList<AbstractConfigValue>(2);
|
List<AbstractConfigValue> concat = new ArrayList<AbstractConfigValue>(2);
|
||||||
AbstractConfigValue previousRef = new ConfigReference(newValue.origin(),
|
AbstractConfigValue previousRef = new ConfigReference(newValue.origin(),
|
||||||
@ -346,7 +311,6 @@ final class ConfigParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SimpleConfigList parseArray(ConfigNodeArray n) {
|
private SimpleConfigList parseArray(ConfigNodeArray n) {
|
||||||
arrayCount += 1;
|
|
||||||
|
|
||||||
SimpleConfigOrigin arrayOrigin = lineOrigin();
|
SimpleConfigOrigin arrayOrigin = lineOrigin();
|
||||||
List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>();
|
List<AbstractConfigValue> values = new ArrayList<AbstractConfigValue>();
|
||||||
@ -356,6 +320,8 @@ final class ConfigParser {
|
|||||||
|
|
||||||
AbstractConfigValue v = null;
|
AbstractConfigValue v = null;
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
for (AbstractConfigNode node : n.children()) {
|
for (AbstractConfigNode node : n.children()) {
|
||||||
if (node instanceof ConfigNodeComment) {
|
if (node instanceof ConfigNodeComment) {
|
||||||
comments.add(((ConfigNodeComment) node).commentText());
|
comments.add(((ConfigNodeComment) node).commentText());
|
||||||
@ -376,14 +342,16 @@ final class ConfigParser {
|
|||||||
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
||||||
comments.clear();
|
comments.clear();
|
||||||
}
|
}
|
||||||
|
pathStack.push(new Path(Integer.toString(index)));
|
||||||
|
index ++;
|
||||||
v = parseValue((AbstractConfigNodeValue)node, comments);
|
v = parseValue((AbstractConfigNodeValue)node, comments);
|
||||||
|
pathStack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// There shouldn't be any comments at this point, but add them just in case
|
// There shouldn't be any comments at this point, but add them just in case
|
||||||
if (v != null) {
|
if (v != null) {
|
||||||
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
values.add(v.withOrigin(v.origin().appendComments(new ArrayList<String>(comments))));
|
||||||
}
|
}
|
||||||
arrayCount -= 1;
|
|
||||||
return new SimpleConfigList(arrayOrigin, values);
|
return new SimpleConfigList(arrayOrigin, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ final class ResolveSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static private ValueWithPath findInObject(AbstractConfigObject obj, Path path) {
|
static private ValueWithPath findInObject(AbstractConfigValue obj, Path path) {
|
||||||
try {
|
try {
|
||||||
// we'll fail if anything along the path can't
|
// we'll fail if anything along the path can't
|
||||||
// be looked at without resolving.
|
// be looked at without resolving.
|
||||||
@ -65,19 +65,34 @@ final class ResolveSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static private ValueWithPath findInObject(AbstractConfigObject obj, Path path, Node<Container> parents) {
|
static private ValueWithPath findInObject(AbstractConfigValue obj, Path path, Node<Container> parents) {
|
||||||
String key = path.first();
|
String key = path.first();
|
||||||
Path next = path.remainder();
|
Path next = path.remainder();
|
||||||
if (ConfigImpl.traceSubstitutionsEnabled())
|
if (ConfigImpl.traceSubstitutionsEnabled())
|
||||||
ConfigImpl.trace("*** looking up '" + key + "' in " + obj);
|
ConfigImpl.trace("*** looking up '" + key + "' in " + obj);
|
||||||
AbstractConfigValue v = obj.attemptPeekWithPartialResolve(key);
|
|
||||||
Node<Container> newParents = parents == null ? new Node<Container>(obj) : parents.prepend(obj);
|
AbstractConfigValue v = null;
|
||||||
|
Node<Container> newParents = null;
|
||||||
|
|
||||||
|
if (obj instanceof AbstractConfigObject) {
|
||||||
|
v = ((AbstractConfigObject) obj).attemptPeekWithPartialResolve(key);
|
||||||
|
newParents = parents == null ? new Node<Container>((AbstractConfigObject) obj) : parents.prepend((AbstractConfigObject) obj);
|
||||||
|
} else if (obj instanceof SimpleConfigList) {
|
||||||
|
int ix;
|
||||||
|
try {
|
||||||
|
ix = Integer.parseInt(key);
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new ConfigException.BadPath(key, "path for list must be numeric index");
|
||||||
|
}
|
||||||
|
v = ((SimpleConfigList) obj).get(ix);
|
||||||
|
newParents = parents == null ? new Node<Container>((SimpleConfigList) obj) : parents.prepend((SimpleConfigList) obj);
|
||||||
|
}
|
||||||
|
|
||||||
if (next == null) {
|
if (next == null) {
|
||||||
return new ValueWithPath(v, newParents);
|
return new ValueWithPath(v, newParents);
|
||||||
} else {
|
} else {
|
||||||
if (v instanceof AbstractConfigObject) {
|
if (v instanceof AbstractConfigObject || v instanceof SimpleConfigList) {
|
||||||
return findInObject((AbstractConfigObject) v, next, newParents);
|
return findInObject(v, next, newParents);
|
||||||
} else {
|
} else {
|
||||||
return new ValueWithPath(null, newParents);
|
return new ValueWithPath(null, newParents);
|
||||||
}
|
}
|
||||||
|
@ -60,4 +60,4 @@ final class SimpleConfigDocument implements ConfigDocument {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return render().hashCode();
|
return render().hashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -692,4 +692,4 @@ final class Tokenizer {
|
|||||||
"Does not make sense to remove items from token stream");
|
"Does not make sense to remove items from token stream");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -136,4 +136,4 @@ public class ArraysConfig {
|
|||||||
public void setOfStringBean(List<StringsConfig> ofStringBean) {
|
public void setOfStringBean(List<StringsConfig> ofStringBean) {
|
||||||
this.ofStringBean = ofStringBean;
|
this.ofStringBean = ofStringBean;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -42,4 +42,4 @@ public class StringsConfig {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "StringsConfig(" + abcd + "," + yes + ")";
|
return "StringsConfig(" + abcd + "," + yes + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -52,17 +52,7 @@
|
|||||||
"ofConfigObject" : [${numbers}, ${booleans}, ${strings}],
|
"ofConfigObject" : [${numbers}, ${booleans}, ${strings}],
|
||||||
"ofConfigValue" : [1, 2, "a"],
|
"ofConfigValue" : [1, 2, "a"],
|
||||||
"ofDuration" : [1, 2h, 3 days],
|
"ofDuration" : [1, 2h, 3 days],
|
||||||
"ofMemorySize" : [1024, 1M, 1G],
|
"ofMemorySize" : [1024, 1M, 1G]
|
||||||
"ofStringBean" : [
|
|
||||||
{
|
|
||||||
abcd : "testAbcdOne"
|
|
||||||
yes : "testYesOne"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
abcd : "testAbcdTwo"
|
|
||||||
yes : "testYesTwo"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"bytes" : {
|
"bytes" : {
|
||||||
"kilobyte" : "1kB",
|
"kilobyte" : "1kB",
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
// The {} inside the [] is needed because
|
// The {} inside the [] is needed because
|
||||||
// just [ include ] means an array with the
|
// just [ include ] means an array with the
|
||||||
// string "include" in it.
|
// string "include" in it.
|
||||||
a = [ { include "test01.conf" } ]
|
a = [
|
||||||
|
{
|
||||||
|
include "test12.conf"
|
||||||
|
replace-key: "replaced"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
3
config/src/test/resources/test12.conf
Normal file
3
config/src/test/resources/test12.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// This file is included by include-from-list.conf to verify that includes with substitutions work in arrays
|
||||||
|
|
||||||
|
replace-me: ${replace-key}
|
@ -345,26 +345,19 @@ class ConcatenationTest extends TestUtils {
|
|||||||
assertEquals(Seq(1, 2, 3), conf.getObjectList("x.a").asScala.toList.map(_.toConfig.getInt("b")))
|
assertEquals(Seq(1, 2, 3), conf.getObjectList("x.a").asScala.toList.map(_.toConfig.getInt("b")))
|
||||||
}
|
}
|
||||||
|
|
||||||
// We would ideally make this case NOT throw an exception but we need to do some work
|
|
||||||
// to get there, see https://github.com/typesafehub/config/issues/160
|
|
||||||
@Test
|
@Test
|
||||||
def plusEqualsMultipleTimesNestedInArray() {
|
def plusEqualsMultipleTimesNestedInArray() {
|
||||||
val e = intercept[ConfigException.Parse] {
|
val conf = parseConfig("""x = [ { a += 1, a += 2, a += 3 } ] """).resolve()
|
||||||
val conf = parseConfig("""x = [ { a += 1, a += 2, a += 3 } ] """).resolve()
|
assertEquals(Seq(1, 2, 3), conf.getObjectList("x").asScala.toVector(0).toConfig.getIntList("a").asScala.toList)
|
||||||
assertEquals(Seq(1, 2, 3), conf.getObjectList("x").asScala.toVector(0).toConfig.getIntList("a").asScala.toList)
|
|
||||||
}
|
|
||||||
assertTrue(e.getMessage.contains("limitation"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We would ideally make this case NOT throw an exception but we need to do some work
|
|
||||||
// to get there, see https://github.com/typesafehub/config/issues/160
|
|
||||||
@Test
|
@Test
|
||||||
def plusEqualsMultipleTimesNestedInPlusEquals() {
|
def plusEqualsMultipleTimesNestedInPlusEquals() {
|
||||||
val e = intercept[ConfigException.Parse] {
|
val e = intercept[ConfigException.BugOrBroken] {
|
||||||
val conf = parseConfig("""x += { a += 1, a += 2, a += 3 } """).resolve()
|
val conf = parseConfig("""x += { a += 1, a += 2, a += 3 } """).resolve()
|
||||||
assertEquals(Seq(1, 2, 3), conf.getObjectList("x").asScala.toVector(0).toConfig.getIntList("a").asScala.toList)
|
assertEquals(Seq(1, 2, 3), conf.getObjectList("x").asScala.toVector(0).toConfig.getIntList("a").asScala.toList)
|
||||||
}
|
}
|
||||||
assertTrue(e.getMessage.contains("limitation"))
|
assertTrue(e.getMessage.contains("did not find"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// from https://github.com/typesafehub/config/issues/177
|
// from https://github.com/typesafehub/config/issues/177
|
||||||
|
@ -801,6 +801,14 @@ class ConfParserTest extends TestUtils {
|
|||||||
assertTrue("including basename URL doesn't load anything", conf.isEmpty())
|
assertTrue("including basename URL doesn't load anything", conf.isEmpty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def includeWithSubstitutionsFromList() {
|
||||||
|
val conf = ConfigFactory.parseString("include file(" + jsonQuotedResourceFile("include-from-list") + ")")
|
||||||
|
|
||||||
|
val resolved = conf.resolve()
|
||||||
|
assertEquals(resolved.getConfigList("a").get(0).getString("replace-me"), "replaced")
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def acceptBOMStartingFile() {
|
def acceptBOMStartingFile() {
|
||||||
// BOM at start of file should be ignored
|
// BOM at start of file should be ignored
|
||||||
|
@ -209,4 +209,4 @@ class ConfigBeanFactoryTest extends TestUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -455,4 +455,4 @@ class ConfigDocumentTest extends TestUtils {
|
|||||||
assertEquals("{ a : {\n \"a\" : 1,\n \"b\" : 2\n } }",
|
assertEquals("{ a : {\n \"a\" : 1,\n \"b\" : 2\n } }",
|
||||||
configDocument.withValue("a", configVal).render)
|
configDocument.withValue("a", configVal).render)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -229,4 +229,4 @@ class ConfigNodeTest extends TestUtils {
|
|||||||
assertEquals(finalText, newNode.render())
|
assertEquals(finalText, newNode.render())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -875,17 +875,6 @@ class PublicApiTest extends TestUtils {
|
|||||||
assertTrue("wrong exception: " + e.getMessage, e.getMessage.contains("include statements nested"))
|
assertTrue("wrong exception: " + e.getMessage, e.getMessage.contains("include statements nested"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// We would ideally make this case NOT throw an exception but we need to do some work
|
|
||||||
// to get there, see https://github.com/typesafehub/config/issues/160
|
|
||||||
@Test
|
|
||||||
def detectIncludeFromList() {
|
|
||||||
val e = intercept[ConfigException.Parse] {
|
|
||||||
ConfigFactory.load("include-from-list.conf")
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue("wrong exception: " + e.getMessage, e.getMessage.contains("limitation"))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def missingOverrideResourceFails() {
|
def missingOverrideResourceFails() {
|
||||||
assertEquals("config.file is not set", null, System.getProperty("config.file"))
|
assertEquals("config.file is not set", null, System.getProperty("config.file"))
|
||||||
|
Loading…
Reference in New Issue
Block a user