mirror of
https://github.com/lightbend/config.git
synced 2025-03-22 15:20:26 +08:00
Cleanup of ConfigDocumentParser and tests
Clean up ConfigDocumentParser tests by using the `intercept` method to handle exception testing, moving some tests, and removing a test that was re-testing already tested functionality. Fix bug wherein parsing of a single value would throw an exception when the value is a map with a key that has an object value and no separator if the default ConfParserOptions where used. Add a test to validate this fix.
This commit is contained in:
parent
7107ec05b0
commit
6c011b5e4a
@ -15,7 +15,8 @@ final class ConfigDocumentParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static AbstractConfigNodeValue parseValue(Iterator<Token> tokens, ConfigOrigin origin, ConfigParseOptions options) {
|
static AbstractConfigNodeValue parseValue(Iterator<Token> tokens, ConfigOrigin origin, ConfigParseOptions options) {
|
||||||
ParseContext context = new ParseContext(options.getSyntax(), origin, tokens);
|
ConfigSyntax syntax = options.getSyntax() == null ? ConfigSyntax.CONF : options.getSyntax();
|
||||||
|
ParseContext context = new ParseContext(syntax, origin, tokens);
|
||||||
return context.parseSingleValue();
|
return context.parseSingleValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,7 +172,7 @@ final class ConfigDocumentParser {
|
|||||||
for (AbstractConfigNode node : values) {
|
for (AbstractConfigNode node : values) {
|
||||||
if (node instanceof AbstractConfigNodeValue)
|
if (node instanceof AbstractConfigNodeValue)
|
||||||
value = (AbstractConfigNodeValue)node;
|
value = (AbstractConfigNodeValue)node;
|
||||||
else if (node == null)
|
else if (value == null)
|
||||||
nodes.add(node);
|
nodes.add(node);
|
||||||
else
|
else
|
||||||
putBack((new ArrayList<Token>(node.tokens())).get(0));
|
putBack((new ArrayList<Token>(node.tokens())).get(0));
|
||||||
@ -183,7 +184,7 @@ final class ConfigDocumentParser {
|
|||||||
// any leading/trailing whitespace
|
// any leading/trailing whitespace
|
||||||
for (int i = values.size() - 1; i >= 0; i--) {
|
for (int i = values.size() - 1; i >= 0; i--) {
|
||||||
if (values.get(i) instanceof ConfigNodeSingleToken) {
|
if (values.get(i) instanceof ConfigNodeSingleToken) {
|
||||||
putBack((new ArrayList<Token>(values.get(i).tokens())).get(0));
|
putBack(((ConfigNodeSingleToken) values.get(i)).token());
|
||||||
values.remove(i);
|
values.remove(i);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
@ -497,7 +498,6 @@ final class ConfigDocumentParser {
|
|||||||
AbstractConfigNodeValue nextValue = consolidateValues(children);
|
AbstractConfigNodeValue nextValue = consolidateValues(children);
|
||||||
if (nextValue != null) {
|
if (nextValue != null) {
|
||||||
children.add(nextValue);
|
children.add(nextValue);
|
||||||
nextValue = null;
|
|
||||||
} else {
|
} else {
|
||||||
t = nextTokenCollectingWhitespace(children);
|
t = nextTokenCollectingWhitespace(children);
|
||||||
|
|
||||||
@ -510,7 +510,6 @@ final class ConfigDocumentParser {
|
|||||||
|| Tokens.isSubstitution(t)) {
|
|| Tokens.isSubstitution(t)) {
|
||||||
nextValue = parseValue(t);
|
nextValue = parseValue(t);
|
||||||
children.add(nextValue);
|
children.add(nextValue);
|
||||||
nextValue = null;
|
|
||||||
} else {
|
} else {
|
||||||
throw parseError("List should have ] or a first element after the open [, instead had token: "
|
throw parseError("List should have ] or a first element after the open [, instead had token: "
|
||||||
+ t
|
+ t
|
||||||
@ -543,7 +542,6 @@ final class ConfigDocumentParser {
|
|||||||
nextValue = consolidateValues(children);
|
nextValue = consolidateValues(children);
|
||||||
if (nextValue != null) {
|
if (nextValue != null) {
|
||||||
children.add(nextValue);
|
children.add(nextValue);
|
||||||
nextValue = null;
|
|
||||||
} else {
|
} else {
|
||||||
t = nextTokenCollectingWhitespace(children);
|
t = nextTokenCollectingWhitespace(children);
|
||||||
if (Tokens.isValue(t) || t == Tokens.OPEN_CURLY
|
if (Tokens.isValue(t) || t == Tokens.OPEN_CURLY
|
||||||
@ -551,7 +549,6 @@ final class ConfigDocumentParser {
|
|||||||
|| Tokens.isSubstitution(t)) {
|
|| Tokens.isSubstitution(t)) {
|
||||||
nextValue = parseValue(t);
|
nextValue = parseValue(t);
|
||||||
children.add(nextValue);
|
children.add(nextValue);
|
||||||
nextValue = null;
|
|
||||||
} else if (flavor != ConfigSyntax.JSON && t == Tokens.CLOSE_SQUARE) {
|
} else if (flavor != ConfigSyntax.JSON && t == Tokens.CLOSE_SQUARE) {
|
||||||
// we allow one trailing comma
|
// we allow one trailing comma
|
||||||
putBack(t);
|
putBack(t);
|
||||||
|
@ -13,65 +13,48 @@ class ConfigDocumentParserTest extends TestUtils {
|
|||||||
|
|
||||||
private def parseJSONFailuresTest(origText: String, containsMessage: String) {
|
private def parseJSONFailuresTest(origText: String, containsMessage: String) {
|
||||||
var exceptionThrown = false
|
var exceptionThrown = false
|
||||||
try {
|
val e = intercept[ConfigException] {
|
||||||
ConfigDocumentParser.parse(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
ConfigDocumentParser.parse(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||||
} catch {
|
|
||||||
case e: Exception =>
|
|
||||||
exceptionThrown = true
|
|
||||||
assertTrue(e.isInstanceOf[ConfigException])
|
|
||||||
assertTrue(e.getMessage.contains(containsMessage))
|
|
||||||
}
|
}
|
||||||
assertTrue(exceptionThrown)
|
assertTrue(e.getMessage.contains(containsMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def parseSimpleValueTest(origText: String, finalText: String = null) {
|
private def parseSimpleValueTest(origText: String, finalText: String = null) {
|
||||||
val expectedRenderedText = if (finalText == null) origText else finalText
|
val expectedRenderedText = if (finalText == null) origText else finalText
|
||||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults())
|
val node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults())
|
||||||
assertEquals(expectedRenderedText, node.render())
|
assertEquals(expectedRenderedText, node.render())
|
||||||
assertTrue(node.isInstanceOf[AbstractConfigNodeValue])
|
assertTrue(node.isInstanceOf[ConfigNodeSimpleValue])
|
||||||
|
|
||||||
val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||||
assertEquals(expectedRenderedText, nodeJSON.render())
|
assertEquals(expectedRenderedText, nodeJSON.render())
|
||||||
assertTrue(nodeJSON.isInstanceOf[AbstractConfigNodeValue])
|
assertTrue(nodeJSON.isInstanceOf[ConfigNodeSimpleValue])
|
||||||
}
|
}
|
||||||
|
|
||||||
private def parseComplexValueTest(origText: String) {
|
private def parseComplexValueTest(origText: String) {
|
||||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults())
|
val node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults())
|
||||||
assertEquals(origText, node.render())
|
assertEquals(origText, node.render())
|
||||||
assertTrue(node.isInstanceOf[AbstractConfigNodeValue])
|
assertTrue(node.isInstanceOf[ConfigNodeComplexValue])
|
||||||
|
|
||||||
val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
val nodeJSON = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||||
assertEquals(origText, nodeJSON.render())
|
assertEquals(origText, nodeJSON.render())
|
||||||
assertTrue(nodeJSON.isInstanceOf[AbstractConfigNodeValue])
|
assertTrue(nodeJSON.isInstanceOf[ConfigNodeComplexValue])
|
||||||
}
|
}
|
||||||
|
|
||||||
private def parseSingleValueInvalidJSONTest(origText: String, containsMessage: String) {
|
private def parseSingleValueInvalidJSONTest(origText: String, containsMessage: String) {
|
||||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults())
|
val node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults())
|
||||||
assertEquals(origText, node.render())
|
assertEquals(origText, node.render())
|
||||||
|
|
||||||
var exceptionThrown = false
|
val e = intercept[ConfigException] {
|
||||||
try {
|
ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
||||||
ConfigDocumentParser.parse(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
|
||||||
} catch {
|
|
||||||
case e: Exception =>
|
|
||||||
exceptionThrown = true
|
|
||||||
assertTrue(e.isInstanceOf[ConfigException])
|
|
||||||
assertTrue(e.getMessage.contains(containsMessage))
|
|
||||||
}
|
}
|
||||||
assertTrue(exceptionThrown)
|
assertTrue(e.getMessage.contains(containsMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def parseLeadingTrailingFailure(toReplace: String) {
|
private def parseLeadingTrailingFailure(toReplace: String) {
|
||||||
var exceptionThrown = false
|
val e = intercept[ConfigException] {
|
||||||
try {
|
|
||||||
ConfigDocumentParser.parseValue(tokenize(toReplace), fakeOrigin(), ConfigParseOptions.defaults())
|
ConfigDocumentParser.parseValue(tokenize(toReplace), fakeOrigin(), ConfigParseOptions.defaults())
|
||||||
} catch {
|
|
||||||
case e: Exception =>
|
|
||||||
exceptionThrown = true
|
|
||||||
assertTrue(e.isInstanceOf[ConfigException])
|
|
||||||
assertTrue(e.getMessage.contains("The value from setValue cannot have leading or trailing newlines, whitespace, or comments"))
|
|
||||||
}
|
}
|
||||||
assertTrue(exceptionThrown)
|
assertTrue(e.getMessage.contains("The value from setValue cannot have leading or trailing newlines, whitespace, or comments"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -212,7 +195,7 @@ class ConfigDocumentParserTest extends TestUtils {
|
|||||||
parseJSONFailuresTest("""{ "foo": 123 456 789 } """, "Expecting close brace } or a comma")
|
parseJSONFailuresTest("""{ "foo": 123 456 789 } """, "Expecting close brace } or a comma")
|
||||||
|
|
||||||
// JSON must begin with { or [
|
// JSON must begin with { or [
|
||||||
parseJSONFailuresTest(""""a": 123, "b": 456"""", "Document must have an object or array at root")
|
parseJSONFailuresTest(""""a": 123, "b": 456""", "Document must have an object or array at root")
|
||||||
|
|
||||||
// JSON does not support unquoted text
|
// JSON does not support unquoted text
|
||||||
parseJSONFailuresTest("""{"foo": unquotedtext}""", "Token not allowed in valid JSON")
|
parseJSONFailuresTest("""{"foo": unquotedtext}""", "Token not allowed in valid JSON")
|
||||||
@ -255,22 +238,14 @@ class ConfigDocumentParserTest extends TestUtils {
|
|||||||
parseComplexValueTest("""["a","b","c"]""")
|
parseComplexValueTest("""["a","b","c"]""")
|
||||||
|
|
||||||
// Check that concatenations are handled by CONF parsing
|
// Check that concatenations are handled by CONF parsing
|
||||||
var origText = "123 456 unquotedtext abc"
|
var origText = "123 456 \"abc\""
|
||||||
var node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults())
|
var node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults())
|
||||||
assertEquals(origText, node.render())
|
assertEquals(origText, node.render())
|
||||||
|
|
||||||
// Check that concatenations in JSON will throw an error
|
// Check that keys with no separators and object values are handled by CONF parsing
|
||||||
origText = "123 456 789"
|
origText = """{"foo" { "bar" : 12 } }"""
|
||||||
var exceptionThrown = false
|
node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults())
|
||||||
try {
|
assertEquals(origText, node.render())
|
||||||
node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
|
||||||
} catch {
|
|
||||||
case e: Exception =>
|
|
||||||
exceptionThrown = true
|
|
||||||
assertTrue(e.isInstanceOf[ConfigException])
|
|
||||||
assertTrue(e.getMessage.contains("Parsing JSON and the value set in setValue was either a concatenation or had trailing whitespace, newlines, or comments"))
|
|
||||||
}
|
|
||||||
assertTrue(exceptionThrown)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -291,17 +266,14 @@ class ConfigDocumentParserTest extends TestUtils {
|
|||||||
parseSingleValueInvalidJSONTest("${a.b}", "Substitutions (${} syntax) not allowed in JSON")
|
parseSingleValueInvalidJSONTest("${a.b}", "Substitutions (${} syntax) not allowed in JSON")
|
||||||
|
|
||||||
// Check that concatenations in JSON will throw an error
|
// Check that concatenations in JSON will throw an error
|
||||||
val origText = "123 456 789"
|
var origText = "123 456 \"abc\""
|
||||||
var exceptionThrown = false
|
var e = intercept[ConfigException] { ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON)) }
|
||||||
try {
|
assertTrue(e.getMessage.contains("Parsing JSON and the value set in setValue was either a concatenation or had trailing whitespace, newlines, or comments"))
|
||||||
val node = ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax(ConfigSyntax.JSON))
|
|
||||||
} catch {
|
// Check that keys with no separators and object values in JSON will throw an error
|
||||||
case e: Exception =>
|
origText = """{"foo" { "bar" : 12 } }"""
|
||||||
exceptionThrown = true
|
e = intercept[ConfigException] { ConfigDocumentParser.parseValue(tokenize(origText), fakeOrigin(), ConfigParseOptions.defaults().setSyntax((ConfigSyntax.JSON))) }
|
||||||
assertTrue(e.isInstanceOf[ConfigException])
|
assertTrue(e.getMessage.contains("""Key '"foo"' may not be followed by token: '{'"""))
|
||||||
assertTrue(e.getMessage.contains("Parsing JSON and the value set in setValue was either a concatenation or had trailing whitespace, newlines, or comments"))
|
|
||||||
}
|
|
||||||
assertTrue(exceptionThrown)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
Reference in New Issue
Block a user