mirror of
https://github.com/lightbend/config.git
synced 2025-03-31 06:30:23 +08:00
add a bunch of small tests, fix related minor bugs
This brings code coverage up to a decent percentage
This commit is contained in:
parent
0ec572c8d9
commit
99d3c17fd0
@ -65,10 +65,18 @@ public final class Config {
|
|||||||
public static long parseDuration(String input,
|
public static long parseDuration(String input,
|
||||||
ConfigOrigin originForException, String pathForException) {
|
ConfigOrigin originForException, String pathForException) {
|
||||||
String s = input.trim();
|
String s = input.trim();
|
||||||
String unitString = getUnits(s);
|
String originalUnitString = getUnits(s);
|
||||||
|
String unitString = originalUnitString;
|
||||||
String numberString = s.substring(0, s.length() - unitString.length()).trim();
|
String numberString = s.substring(0, s.length() - unitString.length()).trim();
|
||||||
TimeUnit units = null;
|
TimeUnit units = null;
|
||||||
|
|
||||||
|
// this would be caught later anyway, but the error message
|
||||||
|
// is more helpful if we check it here.
|
||||||
|
if (numberString.length() == 0)
|
||||||
|
throw new ConfigException.BadValue(originForException,
|
||||||
|
pathForException, "No number in duration value '" + input
|
||||||
|
+ "'");
|
||||||
|
|
||||||
if (unitString.length() > 2 && !unitString.endsWith("s"))
|
if (unitString.length() > 2 && !unitString.endsWith("s"))
|
||||||
unitString = unitString + "s";
|
unitString = unitString + "s";
|
||||||
|
|
||||||
@ -90,7 +98,8 @@ public final class Config {
|
|||||||
} else {
|
} else {
|
||||||
throw new ConfigException.BadValue(originForException,
|
throw new ConfigException.BadValue(originForException,
|
||||||
pathForException, "Could not parse time unit '"
|
pathForException, "Could not parse time unit '"
|
||||||
+ unitString + "' (try ns, us, ms, s, m, d)");
|
+ originalUnitString
|
||||||
|
+ "' (try ns, us, ms, s, m, d)");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -149,6 +158,14 @@ public final class Config {
|
|||||||
String numberString = s.substring(0,
|
String numberString = s.substring(0,
|
||||||
s.length() - unitStringMaybePlural.length())
|
s.length() - unitStringMaybePlural.length())
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
|
// this would be caught later anyway, but the error message
|
||||||
|
// is more helpful if we check it here.
|
||||||
|
if (numberString.length() == 0)
|
||||||
|
throw new ConfigException.BadValue(originForException,
|
||||||
|
pathForException, "No number in size-in-bytes value '"
|
||||||
|
+ input + "'");
|
||||||
|
|
||||||
MemoryUnit units = null;
|
MemoryUnit units = null;
|
||||||
|
|
||||||
// the short abbreviations are case-insensitive but you can't write the
|
// the short abbreviations are case-insensitive but you can't write the
|
||||||
@ -165,7 +182,7 @@ public final class Config {
|
|||||||
} else {
|
} else {
|
||||||
throw new ConfigException.BadValue(originForException,
|
throw new ConfigException.BadValue(originForException,
|
||||||
pathForException, "Could not parse size unit '"
|
pathForException, "Could not parse size unit '"
|
||||||
+ unitString + "' (try b, k, m, g)");
|
+ unitStringMaybePlural + "' (try b, k, m, g)");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -299,14 +299,16 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getMilliseconds(String path) {
|
public Long getMilliseconds(String path) {
|
||||||
return TimeUnit.NANOSECONDS.toMillis(getNanoseconds(path));
|
long ns = getNanoseconds(path);
|
||||||
|
long ms = TimeUnit.NANOSECONDS.toMillis(ns);
|
||||||
|
return ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getNanoseconds(String path) {
|
public Long getNanoseconds(String path) {
|
||||||
Long ns = null;
|
Long ns = null;
|
||||||
try {
|
try {
|
||||||
ns = getLong(path);
|
ns = TimeUnit.MILLISECONDS.toNanos(getLong(path));
|
||||||
} catch (ConfigException.WrongType e) {
|
} catch (ConfigException.WrongType e) {
|
||||||
ConfigValue v = resolve(path, ConfigValueType.STRING, path);
|
ConfigValue v = resolve(path, ConfigValueType.STRING, path);
|
||||||
ns = Config.parseDuration((String) v.unwrapped(), v.origin(), path);
|
ns = Config.parseDuration((String) v.unwrapped(), v.origin(), path);
|
||||||
@ -438,7 +440,8 @@ abstract class AbstractConfigObject extends AbstractConfigValue implements
|
|||||||
List<? extends ConfigValue> list = getList(path);
|
List<? extends ConfigValue> list = getList(path);
|
||||||
for (ConfigValue v : list) {
|
for (ConfigValue v : list) {
|
||||||
if (v.valueType() == ConfigValueType.NUMBER) {
|
if (v.valueType() == ConfigValueType.NUMBER) {
|
||||||
l.add(((Number) v.unwrapped()).longValue());
|
l.add(TimeUnit.MILLISECONDS.toNanos(((Number) v.unwrapped())
|
||||||
|
.longValue()));
|
||||||
} else if (v.valueType() == ConfigValueType.STRING) {
|
} else if (v.valueType() == ConfigValueType.STRING) {
|
||||||
String s = (String) v.unwrapped();
|
String s = (String) v.unwrapped();
|
||||||
Long n = Config.parseDuration(s, v.origin(), path);
|
Long n = Config.parseDuration(s, v.origin(), path);
|
||||||
|
@ -66,7 +66,11 @@ abstract class AbstractConfigValue implements ConfigValue {
|
|||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
// note that "origin" is deliberately NOT part of equality
|
// note that "origin" is deliberately NOT part of equality
|
||||||
return this.unwrapped().hashCode();
|
Object o = this.unwrapped();
|
||||||
|
if (o == null)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return o.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.typesafe.config.impl;
|
package com.typesafe.config.impl;
|
||||||
|
|
||||||
import com.typesafe.config.ConfigException;
|
import com.typesafe.config.ConfigException;
|
||||||
import com.typesafe.config.ConfigObject;
|
|
||||||
import com.typesafe.config.ConfigOrigin;
|
import com.typesafe.config.ConfigOrigin;
|
||||||
import com.typesafe.config.ConfigValueType;
|
import com.typesafe.config.ConfigValueType;
|
||||||
|
|
||||||
@ -23,12 +22,9 @@ final class Tokens {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
String s = tokenType().name() + "(" + value.valueType().name()
|
String s = tokenType().name() + "(" + value.valueType().name()
|
||||||
+ ")";
|
+ ")";
|
||||||
if (value instanceof ConfigObject || value instanceof ConfigList) {
|
|
||||||
return s;
|
|
||||||
} else {
|
|
||||||
return s + "='" + value().unwrapped() + "'";
|
return s + "='" + value().unwrapped() + "'";
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean canEqual(Object other) {
|
protected boolean canEqual(Object other) {
|
||||||
|
@ -17,7 +17,13 @@
|
|||||||
"c" : "c",
|
"c" : "c",
|
||||||
"d" : "d",
|
"d" : "d",
|
||||||
"concatenated" : null bar 42 baz true 3.14 hi,
|
"concatenated" : null bar 42 baz true 3.14 hi,
|
||||||
"number" : "57"
|
"double" : "3.14",
|
||||||
|
"number" : "57",
|
||||||
|
"null" : "null",
|
||||||
|
"true" : "true",
|
||||||
|
"yes" : "yes",
|
||||||
|
"false" : "false",
|
||||||
|
"no" : "no"
|
||||||
},
|
},
|
||||||
|
|
||||||
"arrays" : {
|
"arrays" : {
|
||||||
@ -45,11 +51,15 @@
|
|||||||
|
|
||||||
"durations" : {
|
"durations" : {
|
||||||
"second" : 1s,
|
"second" : 1s,
|
||||||
"secondsList" : [1s,2seconds,3 s]
|
"secondsList" : [1s,2seconds,3 s, 4000],
|
||||||
|
"secondAsNumber" : 1000,
|
||||||
|
"halfSecond" : 0.5s
|
||||||
},
|
},
|
||||||
|
|
||||||
"memsizes" : {
|
"memsizes" : {
|
||||||
"meg" : 1M,
|
"meg" : 1M,
|
||||||
"megsList" : [1M, 1024K]
|
"megsList" : [1M, 1024K, 1048576],
|
||||||
|
"megAsNumber" : 1048576,
|
||||||
|
"halfMeg" : 0.5M
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,6 @@ import com.typesafe.config.ConfigException
|
|||||||
|
|
||||||
class ConfigSubstitutionTest extends TestUtils {
|
class ConfigSubstitutionTest extends TestUtils {
|
||||||
|
|
||||||
private def subst(ref: String, style: SubstitutionStyle = SubstitutionStyle.PATH) = {
|
|
||||||
val pieces = java.util.Collections.singletonList[Object](new Substitution(ref, style))
|
|
||||||
new ConfigSubstitution(fakeOrigin(), pieces)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def substInString(ref: String, style: SubstitutionStyle = SubstitutionStyle.PATH) = {
|
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
val pieces = List("start<", new Substitution(ref, style), ">end")
|
|
||||||
new ConfigSubstitution(fakeOrigin(), pieces.asJava)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def resolveWithoutFallbacks(v: AbstractConfigObject) = {
|
private def resolveWithoutFallbacks(v: AbstractConfigObject) = {
|
||||||
SubstitutionResolver.resolveWithoutFallbacks(v, v).asInstanceOf[AbstractConfigObject]
|
SubstitutionResolver.resolveWithoutFallbacks(v, v).asInstanceOf[AbstractConfigObject]
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import com.typesafe.config.ConfigObject
|
|||||||
import com.typesafe.config.ConfigException
|
import com.typesafe.config.ConfigException
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
|
import com.typesafe.config.ConfigConfig
|
||||||
|
|
||||||
class ConfigTest extends TestUtils {
|
class ConfigTest extends TestUtils {
|
||||||
|
|
||||||
@ -141,7 +142,7 @@ class ConfigTest extends TestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
def test01() {
|
def test01Getting() {
|
||||||
val conf = Config.load("test01")
|
val conf = Config.load("test01")
|
||||||
|
|
||||||
// get all the primitive types
|
// get all the primitive types
|
||||||
@ -158,6 +159,11 @@ class ConfigTest extends TestUtils {
|
|||||||
// FIXME need to add a way to get a null
|
// FIXME need to add a way to get a null
|
||||||
//assertEquals(null, conf.getAny("nulls.null"))
|
//assertEquals(null, conf.getAny("nulls.null"))
|
||||||
|
|
||||||
|
// get stuff with getAny
|
||||||
|
assertEquals(42L, conf.getAny("ints.fortyTwo"))
|
||||||
|
assertEquals("abcd", conf.getAny("strings.abcd"))
|
||||||
|
assertEquals(false, conf.getAny("booleans.falseAgain"))
|
||||||
|
|
||||||
// get empty array as any type of array
|
// get empty array as any type of array
|
||||||
assertEquals(Seq(), conf.getAnyList("arrays.empty").asScala)
|
assertEquals(Seq(), conf.getAnyList("arrays.empty").asScala)
|
||||||
assertEquals(Seq(), conf.getIntList("arrays.empty").asScala)
|
assertEquals(Seq(), conf.getIntList("arrays.empty").asScala)
|
||||||
@ -184,6 +190,11 @@ class ConfigTest extends TestUtils {
|
|||||||
// plain getList should work
|
// plain getList should work
|
||||||
assertEquals(Seq(intValue(1), intValue(2), intValue(3)), conf.getList("arrays.ofInt").asScala)
|
assertEquals(Seq(intValue(1), intValue(2), intValue(3)), conf.getList("arrays.ofInt").asScala)
|
||||||
assertEquals(Seq(stringValue("a"), stringValue("b"), stringValue("c")), conf.getList("arrays.ofString").asScala)
|
assertEquals(Seq(stringValue("a"), stringValue("b"), stringValue("c")), conf.getList("arrays.ofString").asScala)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def test01Exceptions() {
|
||||||
|
val conf = Config.load("test01")
|
||||||
|
|
||||||
// should throw Missing if key doesn't exist
|
// should throw Missing if key doesn't exist
|
||||||
intercept[ConfigException.Missing] {
|
intercept[ConfigException.Missing] {
|
||||||
@ -195,31 +206,145 @@ class ConfigTest extends TestUtils {
|
|||||||
conf.getInt("nulls.null")
|
conf.getInt("nulls.null")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.Null] {
|
||||||
|
conf.getIntList("nulls.null")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.Null] {
|
||||||
|
conf.getMilliseconds("nulls.null")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.Null] {
|
||||||
|
conf.getNanoseconds("nulls.null")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.Null] {
|
||||||
|
conf.getMemorySize("nulls.null")
|
||||||
|
}
|
||||||
|
|
||||||
// should throw WrongType if key is wrong type and not convertible
|
// should throw WrongType if key is wrong type and not convertible
|
||||||
intercept[ConfigException.WrongType] {
|
intercept[ConfigException.WrongType] {
|
||||||
conf.getInt("booleans.trueAgain")
|
conf.getInt("booleans.trueAgain")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getBooleanList("arrays.ofInt")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getIntList("arrays.ofBoolean")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getObjectList("arrays.ofInt")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getMilliseconds("ints")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getNanoseconds("ints")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getMemorySize("ints")
|
||||||
|
}
|
||||||
|
|
||||||
|
// should throw BadPath on various bad paths
|
||||||
|
intercept[ConfigException.BadPath] {
|
||||||
|
conf.getInt(".bad")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.BadPath] {
|
||||||
|
conf.getInt("bad.")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.BadPath] {
|
||||||
|
conf.getInt("bad..bad")
|
||||||
|
}
|
||||||
|
|
||||||
|
// should throw BadValue on things that don't parse
|
||||||
|
// as durations and sizes
|
||||||
|
intercept[ConfigException.BadValue] {
|
||||||
|
conf.getMilliseconds("strings.a")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.BadValue] {
|
||||||
|
conf.getNanoseconds("strings.a")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.BadValue] {
|
||||||
|
conf.getMemorySize("strings.a")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def test01Conversions() {
|
||||||
|
val conf = Config.load("test01")
|
||||||
|
|
||||||
// should convert numbers to string
|
// should convert numbers to string
|
||||||
assertEquals("42", conf.getString("ints.fortyTwo"))
|
assertEquals("42", conf.getString("ints.fortyTwo"))
|
||||||
assertEquals("42.1", conf.getString("floats.fortyTwoPointOne"))
|
assertEquals("42.1", conf.getString("floats.fortyTwoPointOne"))
|
||||||
|
|
||||||
// should convert string to number
|
// should convert string to number
|
||||||
assertEquals(57, conf.getInt("strings.number"))
|
assertEquals(57, conf.getInt("strings.number"))
|
||||||
|
assertEquals(3.14, conf.getDouble("strings.double"), 1e-6)
|
||||||
|
|
||||||
|
// should convert strings to boolean
|
||||||
|
assertEquals(true, conf.getBoolean("strings.true"))
|
||||||
|
assertEquals(true, conf.getBoolean("strings.yes"))
|
||||||
|
assertEquals(false, conf.getBoolean("strings.false"))
|
||||||
|
assertEquals(false, conf.getBoolean("strings.no"))
|
||||||
|
|
||||||
|
// converting some random string to boolean fails though
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getBoolean("strings.abcd")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME test convert string "null" to a null value
|
||||||
|
|
||||||
|
// should not convert strings to object or list
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getObject("strings.a")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getList("strings.a")
|
||||||
|
}
|
||||||
|
|
||||||
|
// should not convert object or list to string
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getString("ints")
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept[ConfigException.WrongType] {
|
||||||
|
conf.getString("arrays.ofInt")
|
||||||
|
}
|
||||||
|
|
||||||
// should get durations
|
// should get durations
|
||||||
def asNanos(secs: Int) = TimeUnit.SECONDS.toNanos(secs)
|
def asNanos(secs: Int) = TimeUnit.SECONDS.toNanos(secs)
|
||||||
assertEquals(1000L, conf.getMilliseconds("durations.second"))
|
assertEquals(1000L, conf.getMilliseconds("durations.second"))
|
||||||
assertEquals(asNanos(1), conf.getNanoseconds("durations.second"))
|
assertEquals(asNanos(1), conf.getNanoseconds("durations.second"))
|
||||||
assertEquals(Seq(1000L, 2000L, 3000L),
|
assertEquals(1000L, conf.getMilliseconds("durations.secondAsNumber"))
|
||||||
|
assertEquals(asNanos(1), conf.getNanoseconds("durations.secondAsNumber"))
|
||||||
|
assertEquals(Seq(1000L, 2000L, 3000L, 4000L),
|
||||||
conf.getMillisecondsList("durations.secondsList").asScala)
|
conf.getMillisecondsList("durations.secondsList").asScala)
|
||||||
assertEquals(Seq(asNanos(1), asNanos(2), asNanos(3)),
|
assertEquals(Seq(asNanos(1), asNanos(2), asNanos(3), asNanos(4)),
|
||||||
conf.getNanosecondsList("durations.secondsList").asScala)
|
conf.getNanosecondsList("durations.secondsList").asScala)
|
||||||
|
assertEquals(500L, conf.getMilliseconds("durations.halfSecond"))
|
||||||
|
|
||||||
// should get size in bytes
|
// should get size in bytes
|
||||||
assertEquals(1024 * 1024L, conf.getMemorySize("memsizes.meg"))
|
assertEquals(1024 * 1024L, conf.getMemorySize("memsizes.meg"))
|
||||||
assertEquals(Seq(1024 * 1024L, 1024 * 1024L),
|
assertEquals(1024 * 1024L, conf.getMemorySize("memsizes.megAsNumber"))
|
||||||
|
assertEquals(Seq(1024 * 1024L, 1024 * 1024L, 1024L * 1024L),
|
||||||
conf.getMemorySizeList("memsizes.megsList").asScala)
|
conf.getMemorySizeList("memsizes.megsList").asScala)
|
||||||
|
assertEquals(512 * 1024L, conf.getMemorySize("memsizes.halfMeg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def test01MergingOtherFormats() {
|
||||||
|
val conf = Config.load("test01")
|
||||||
|
|
||||||
// should have loaded stuff from .json
|
// should have loaded stuff from .json
|
||||||
assertEquals(1, conf.getInt("fromJson1"))
|
assertEquals(1, conf.getInt("fromJson1"))
|
||||||
@ -229,8 +354,18 @@ class ConfigTest extends TestUtils {
|
|||||||
assertEquals("abc", conf.getString("fromProps.abc"))
|
assertEquals("abc", conf.getString("fromProps.abc"))
|
||||||
assertEquals(1, conf.getInt("fromProps.one"))
|
assertEquals(1, conf.getInt("fromProps.one"))
|
||||||
assertEquals(true, conf.getBoolean("fromProps.bool"))
|
assertEquals(true, conf.getBoolean("fromProps.bool"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def test01ToString() {
|
||||||
|
val conf = Config.load("test01")
|
||||||
|
|
||||||
// toString() on conf objects doesn't throw (toString is just a debug string so not testing its result)
|
// toString() on conf objects doesn't throw (toString is just a debug string so not testing its result)
|
||||||
conf.toString()
|
conf.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def test01LoadWithConfigConfig() {
|
||||||
|
val conf = Config.load(new ConfigConfig("test01"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package com.typesafe.config.impl
|
|||||||
import org.junit.Assert._
|
import org.junit.Assert._
|
||||||
import org.junit._
|
import org.junit._
|
||||||
import com.typesafe.config.ConfigValue
|
import com.typesafe.config.ConfigValue
|
||||||
|
import java.util.Collections
|
||||||
|
|
||||||
class ConfigValueTest extends TestUtils {
|
class ConfigValueTest extends TestUtils {
|
||||||
|
|
||||||
@ -75,4 +76,31 @@ class ConfigValueTest extends TestUtils {
|
|||||||
checkNotEqualObjects(a, differentRef)
|
checkNotEqualObjects(a, differentRef)
|
||||||
checkNotEqualObjects(a, differentStyle)
|
checkNotEqualObjects(a, differentStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def configSubstitutionEquality() {
|
||||||
|
val a = subst("foo")
|
||||||
|
val sameAsA = subst("foo")
|
||||||
|
val b = subst("bar")
|
||||||
|
|
||||||
|
checkEqualObjects(a, a)
|
||||||
|
checkEqualObjects(a, sameAsA)
|
||||||
|
checkNotEqualObjects(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def valuesToString() {
|
||||||
|
// just check that these don't throw, the exact output
|
||||||
|
// isn't super important since it's just for debugging
|
||||||
|
intValue(10).toString()
|
||||||
|
longValue(11).toString()
|
||||||
|
doubleValue(3.14).toString()
|
||||||
|
stringValue("hi").toString()
|
||||||
|
nullValue().toString()
|
||||||
|
boolValue(true).toString()
|
||||||
|
(new SimpleConfigObject(fakeOrigin(), null, Collections.emptyMap[String, AbstractConfigValue]())).toString()
|
||||||
|
(new ConfigList(fakeOrigin(), Collections.emptyList[AbstractConfigValue]())).toString()
|
||||||
|
subst("a").toString()
|
||||||
|
substInString("b").toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,9 @@ import java.io.Reader
|
|||||||
import java.io.StringReader
|
import java.io.StringReader
|
||||||
import com.typesafe.config._
|
import com.typesafe.config._
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
|
import java.util.Collections
|
||||||
|
|
||||||
class ParseTest extends TestUtils {
|
class JsonTest extends TestUtils {
|
||||||
|
|
||||||
def parse(s: String): ConfigValue = {
|
def parse(s: String): ConfigValue = {
|
||||||
Parser.parse(SyntaxFlavor.JSON, new SimpleConfigOrigin("test json string"), s)
|
Parser.parse(SyntaxFlavor.JSON, new SimpleConfigOrigin("test json string"), s)
|
||||||
@ -133,9 +134,13 @@ class ParseTest extends TestUtils {
|
|||||||
def validJsonWorks(): Unit = {
|
def validJsonWorks(): Unit = {
|
||||||
// be sure we do the same thing as Lift when we build our JSON "DOM"
|
// be sure we do the same thing as Lift when we build our JSON "DOM"
|
||||||
for (valid <- whitespaceVariations(validJson)) {
|
for (valid <- whitespaceVariations(validJson)) {
|
||||||
val liftAST = addOffendingJsonToException("lift", valid.test) {
|
val liftAST = if (valid.liftBehaviorUnexpected) {
|
||||||
|
new SimpleConfigObject(fakeOrigin(), null, Collections.emptyMap[String, AbstractConfigValue]())
|
||||||
|
} else {
|
||||||
|
addOffendingJsonToException("lift", valid.test) {
|
||||||
fromJsonWithLiftParser(valid.test)
|
fromJsonWithLiftParser(valid.test)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
val ourAST = addOffendingJsonToException("config-json", valid.test) {
|
val ourAST = addOffendingJsonToException("config-json", valid.test) {
|
||||||
parse(valid.test)
|
parse(valid.test)
|
||||||
}
|
}
|
||||||
|
@ -32,16 +32,43 @@ abstract trait TestUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class NotEqualToAnythingElse {
|
||||||
|
override def equals(other: Any) = {
|
||||||
|
other match {
|
||||||
|
case x: NotEqualToAnythingElse => true
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def hashCode() = 971
|
||||||
|
}
|
||||||
|
|
||||||
|
private object notEqualToAnything extends NotEqualToAnythingElse
|
||||||
|
|
||||||
|
private def checkNotEqualToRandomOtherThing(a: Any) {
|
||||||
|
assertFalse(a.equals(notEqualToAnything))
|
||||||
|
assertFalse(notEqualToAnything.equals(a))
|
||||||
|
}
|
||||||
|
|
||||||
protected def checkNotEqualObjects(a: Any, b: Any) {
|
protected def checkNotEqualObjects(a: Any, b: Any) {
|
||||||
assertFalse(a.equals(b))
|
assertFalse(a.equals(b))
|
||||||
assertFalse(b.equals(a))
|
assertFalse(b.equals(a))
|
||||||
|
// hashcode inequality isn't guaranteed, but
|
||||||
|
// as long as it happens to work it might
|
||||||
|
// detect a bug (if hashcodes are equal,
|
||||||
|
// check if it's due to a bug or correct
|
||||||
|
// before you remove this)
|
||||||
assertFalse(a.hashCode() == b.hashCode())
|
assertFalse(a.hashCode() == b.hashCode())
|
||||||
|
checkNotEqualToRandomOtherThing(a)
|
||||||
|
checkNotEqualToRandomOtherThing(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def checkEqualObjects(a: Any, b: Any) {
|
protected def checkEqualObjects(a: Any, b: Any) {
|
||||||
assertTrue(a.equals(b))
|
assertTrue(a.equals(b))
|
||||||
assertTrue(b.equals(a))
|
assertTrue(b.equals(a))
|
||||||
assertTrue(a.hashCode() == b.hashCode())
|
assertTrue(a.hashCode() == b.hashCode())
|
||||||
|
checkNotEqualToRandomOtherThing(a)
|
||||||
|
checkNotEqualToRandomOtherThing(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
def fakeOrigin() = {
|
def fakeOrigin() = {
|
||||||
@ -68,13 +95,20 @@ abstract trait TestUtils {
|
|||||||
"\"", // single quote by itself
|
"\"", // single quote by itself
|
||||||
"{ \"foo\" : }", // no value in object
|
"{ \"foo\" : }", // no value in object
|
||||||
"{ : 10 }", // no key in object
|
"{ : 10 }", // no key in object
|
||||||
|
"{ \"foo\" }", // no value or colon
|
||||||
|
"{ \"a\" : [ }", // [ is not a valid value
|
||||||
|
"{ \"foo\" : 10, }", // extra trailing comma
|
||||||
|
"{ \"foo\" : 10, true }", // non-key after comma
|
||||||
"{ foo : \"bar\" }", // no quotes on key
|
"{ foo : \"bar\" }", // no quotes on key
|
||||||
"{ foo : bar }", // no quotes on key or value
|
"{ foo : bar }", // no quotes on key or value
|
||||||
|
"[ 1, \\", // ends with backslash
|
||||||
// these two problems are ignored by the lift tokenizer
|
// these two problems are ignored by the lift tokenizer
|
||||||
"[:\"foo\", \"bar\"]", // colon in an array; lift doesn't throw (tokenizer erases it)
|
"[:\"foo\", \"bar\"]", // colon in an array; lift doesn't throw (tokenizer erases it)
|
||||||
"[\"foo\" : \"bar\"]", // colon in an array another way, lift ignores (tokenizer erases it)
|
"[\"foo\" : \"bar\"]", // colon in an array another way, lift ignores (tokenizer erases it)
|
||||||
"[ 10e3e3 ]", // two exponents. ideally this might parse to a number plus string "e3" but it's hard to implement.
|
"[ 10e3e3 ]", // two exponents. ideally this might parse to a number plus string "e3" but it's hard to implement.
|
||||||
|
"[ 1-e3 ]", // malformed number but all chars can appear in a number
|
||||||
"[ \"hello ]", // unterminated string
|
"[ \"hello ]", // unterminated string
|
||||||
|
"[ 1, 2, 3, ]", // array with empty element
|
||||||
ParseTest(true, "{ \"foo\" , true }"), // comma instead of colon, lift is fine with this
|
ParseTest(true, "{ \"foo\" , true }"), // comma instead of colon, lift is fine with this
|
||||||
ParseTest(true, "{ \"foo\" : true \"bar\" : false }"), // missing comma between fields, lift fine with this
|
ParseTest(true, "{ \"foo\" : true \"bar\" : false }"), // missing comma between fields, lift fine with this
|
||||||
"[ 10, }]", // array with } as an element
|
"[ 10, }]", // array with } as an element
|
||||||
@ -87,11 +121,16 @@ abstract trait TestUtils {
|
|||||||
"[]true", // trailing valid token after the root array
|
"[]true", // trailing valid token after the root array
|
||||||
"[${]", // unclosed substitution
|
"[${]", // unclosed substitution
|
||||||
"[$]", // '$' by itself
|
"[$]", // '$' by itself
|
||||||
|
"[$ ]", // '$' by itself with spaces after
|
||||||
|
"""${"foo""bar"}""", // multiple strings in substitution
|
||||||
|
"""{ "a" : [1,2], "b" : y${a}z }""", // trying to interpolate an array in a string
|
||||||
|
"""{ "a" : { "c" : 2 }, "b" : y${a}z }""", // trying to interpolate an object in a string
|
||||||
ParseTest(false, true, "[${ foo.bar}]"), // substitution with leading spaces
|
ParseTest(false, true, "[${ foo.bar}]"), // substitution with leading spaces
|
||||||
ParseTest(false, true, "[${foo.bar }]"), // substitution with trailing spaces
|
ParseTest(false, true, "[${foo.bar }]"), // substitution with trailing spaces
|
||||||
ParseTest(false, true, "[${ \"foo.bar\"}]"), // substitution with leading spaces and quoted
|
ParseTest(false, true, "[${ \"foo.bar\"}]"), // substitution with leading spaces and quoted
|
||||||
ParseTest(false, true, "[${\"foo.bar\" }]"), // substitution with trailing spaces and quoted
|
ParseTest(false, true, "[${\"foo.bar\" }]"), // substitution with trailing spaces and quoted
|
||||||
"[${true}]", // substitution with unquoted true token
|
"[${true}]", // substitution with unquoted true token
|
||||||
|
"[ = ]", // = is not a valid token
|
||||||
"") // empty document again, just for clean formatting of this list ;-)
|
"") // empty document again, just for clean formatting of this list ;-)
|
||||||
|
|
||||||
// We'll automatically try each of these with whitespace modifications
|
// We'll automatically try each of these with whitespace modifications
|
||||||
@ -130,6 +169,8 @@ abstract trait TestUtils {
|
|||||||
"[ ${foo} ]",
|
"[ ${foo} ]",
|
||||||
"[ ${\"foo\"} ]",
|
"[ ${\"foo\"} ]",
|
||||||
"[ ${foo.bar} ]",
|
"[ ${foo.bar} ]",
|
||||||
|
"[ abc xyz ${foo.bar} qrs tuv ]", // value concatenation
|
||||||
|
"[ 1, 2, 3, blah ]",
|
||||||
"[ ${\"foo.bar\"} ]")
|
"[ ${\"foo.bar\"} ]")
|
||||||
|
|
||||||
protected val invalidJson = validConfInvalidJson ++ invalidJsonInvalidConf;
|
protected val invalidJson = validConfInvalidJson ++ invalidJsonInvalidConf;
|
||||||
@ -152,13 +193,16 @@ abstract trait TestUtils {
|
|||||||
if (t.whitespaceMatters) {
|
if (t.whitespaceMatters) {
|
||||||
return Seq(t)
|
return Seq(t)
|
||||||
} else {
|
} else {
|
||||||
for (v <- variations)
|
val withNonAscii = ParseTest(true,
|
||||||
yield ParseTest(t.liftBehaviorUnexpected, v(t.test))
|
t.test.replace(" ", "\u2003")) // 2003 = em space, to test non-ascii whitespace
|
||||||
|
Seq(withNonAscii) ++ (for (v <- variations)
|
||||||
|
yield ParseTest(t.liftBehaviorUnexpected, v(t.test)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def intValue(i: Int) = new ConfigInt(fakeOrigin(), i)
|
protected def intValue(i: Int) = new ConfigInt(fakeOrigin(), i)
|
||||||
|
protected def longValue(l: Long) = new ConfigLong(fakeOrigin(), l)
|
||||||
protected def boolValue(b: Boolean) = new ConfigBoolean(fakeOrigin(), b)
|
protected def boolValue(b: Boolean) = new ConfigBoolean(fakeOrigin(), b)
|
||||||
protected def nullValue() = new ConfigNull(fakeOrigin())
|
protected def nullValue() = new ConfigNull(fakeOrigin())
|
||||||
protected def stringValue(s: String) = new ConfigString(fakeOrigin(), s)
|
protected def stringValue(s: String) = new ConfigString(fakeOrigin(), s)
|
||||||
@ -168,4 +212,25 @@ abstract trait TestUtils {
|
|||||||
Parser.parse(SyntaxFlavor.CONF, new SimpleConfigOrigin("test string"), s).asInstanceOf[AbstractConfigObject]
|
Parser.parse(SyntaxFlavor.CONF, new SimpleConfigOrigin("test string"), s).asInstanceOf[AbstractConfigObject]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected def subst(ref: String, style: SubstitutionStyle = SubstitutionStyle.PATH) = {
|
||||||
|
val pieces = java.util.Collections.singletonList[Object](new Substitution(ref, style))
|
||||||
|
new ConfigSubstitution(fakeOrigin(), pieces)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def substInString(ref: String, style: SubstitutionStyle = SubstitutionStyle.PATH) = {
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
val pieces = List("start<", new Substitution(ref, style), ">end")
|
||||||
|
new ConfigSubstitution(fakeOrigin(), pieces.asJava)
|
||||||
|
}
|
||||||
|
|
||||||
|
def tokenTrue = Tokens.newBoolean(fakeOrigin(), true)
|
||||||
|
def tokenFalse = Tokens.newBoolean(fakeOrigin(), false)
|
||||||
|
def tokenNull = Tokens.newNull(fakeOrigin())
|
||||||
|
def tokenUnquoted(s: String) = Tokens.newUnquotedText(fakeOrigin(), s)
|
||||||
|
def tokenKeySubstitution(s: String) = Tokens.newSubstitution(fakeOrigin(), s, SubstitutionStyle.KEY)
|
||||||
|
def tokenPathSubstitution(s: String) = Tokens.newSubstitution(fakeOrigin(), s, SubstitutionStyle.PATH)
|
||||||
|
def tokenString(s: String) = Tokens.newString(fakeOrigin(), s)
|
||||||
|
def tokenDouble(d: Double) = Tokens.newDouble(fakeOrigin(), d)
|
||||||
|
def tokenInt(i: Int) = Tokens.newInt(fakeOrigin(), i)
|
||||||
|
def tokenLong(l: Long) = Tokens.newLong(fakeOrigin(), l)
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,78 @@ class TokenTest extends TestUtils {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
def tokenEquality() {
|
def tokenEquality() {
|
||||||
|
// syntax tokens
|
||||||
checkEqualObjects(Tokens.START, Tokens.START)
|
checkEqualObjects(Tokens.START, Tokens.START)
|
||||||
checkNotEqualObjects(Tokens.START, Tokens.OPEN_CURLY)
|
checkNotEqualObjects(Tokens.START, Tokens.OPEN_CURLY)
|
||||||
|
|
||||||
checkEqualObjects(Tokens.newInt(fakeOrigin(), 42), Tokens.newInt(fakeOrigin(), 42))
|
// int
|
||||||
checkNotEqualObjects(Tokens.newInt(fakeOrigin(), 42), Tokens.newInt(fakeOrigin(), 43))
|
checkEqualObjects(tokenInt(42), tokenInt(42))
|
||||||
|
checkNotEqualObjects(tokenInt(42), tokenInt(43))
|
||||||
|
|
||||||
|
// long
|
||||||
|
checkEqualObjects(tokenLong(42), tokenLong(42))
|
||||||
|
checkNotEqualObjects(tokenLong(42), tokenLong(43))
|
||||||
|
|
||||||
|
// int and long mixed
|
||||||
|
checkEqualObjects(tokenInt(42), tokenLong(42))
|
||||||
|
checkNotEqualObjects(tokenInt(42), tokenLong(43))
|
||||||
|
|
||||||
|
// boolean
|
||||||
|
checkEqualObjects(tokenTrue, tokenTrue)
|
||||||
|
checkNotEqualObjects(tokenTrue, tokenFalse)
|
||||||
|
|
||||||
|
// double
|
||||||
|
checkEqualObjects(tokenDouble(3.14), tokenDouble(3.14))
|
||||||
|
checkNotEqualObjects(tokenDouble(3.14), tokenDouble(4.14))
|
||||||
|
|
||||||
|
// string
|
||||||
|
checkEqualObjects(tokenString("foo"), tokenString("foo"))
|
||||||
|
checkNotEqualObjects(tokenString("foo"), tokenString("bar"))
|
||||||
|
|
||||||
|
// unquoted
|
||||||
|
checkEqualObjects(tokenUnquoted("foo"), tokenUnquoted("foo"))
|
||||||
|
checkNotEqualObjects(tokenUnquoted("foo"), tokenUnquoted("bar"))
|
||||||
|
|
||||||
|
// key subst
|
||||||
|
checkEqualObjects(tokenKeySubstitution("foo"), tokenKeySubstitution("foo"))
|
||||||
|
checkNotEqualObjects(tokenKeySubstitution("foo"), tokenKeySubstitution("bar"))
|
||||||
|
|
||||||
|
// path subst
|
||||||
|
checkEqualObjects(tokenPathSubstitution("foo"), tokenPathSubstitution("foo"))
|
||||||
|
checkNotEqualObjects(tokenPathSubstitution("foo"), tokenPathSubstitution("bar"))
|
||||||
|
|
||||||
|
// key and path not equal
|
||||||
|
checkNotEqualObjects(tokenKeySubstitution("foo"), tokenPathSubstitution("foo"))
|
||||||
|
|
||||||
|
// null
|
||||||
|
checkEqualObjects(tokenNull, tokenNull)
|
||||||
|
|
||||||
|
// newline
|
||||||
|
checkEqualObjects(Tokens.newLine(10), Tokens.newLine(10))
|
||||||
|
checkNotEqualObjects(Tokens.newLine(10), Tokens.newLine(11))
|
||||||
|
|
||||||
|
// different types are not equal
|
||||||
|
checkNotEqualObjects(tokenTrue, tokenInt(1))
|
||||||
|
checkNotEqualObjects(tokenString("true"), tokenTrue)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
def tokenToString() {
|
||||||
|
// just be sure toString() doesn't throw, it's for debugging
|
||||||
|
// so its exact output doesn't matter a lot
|
||||||
|
tokenTrue.toString()
|
||||||
|
tokenFalse.toString()
|
||||||
|
tokenInt(42).toString()
|
||||||
|
tokenLong(43).toString()
|
||||||
|
tokenDouble(3.14).toString()
|
||||||
|
tokenNull.toString()
|
||||||
|
tokenUnquoted("foo").toString()
|
||||||
|
tokenString("bar").toString()
|
||||||
|
tokenKeySubstitution("a").toString()
|
||||||
|
tokenPathSubstitution("b").toString()
|
||||||
|
Tokens.newLine(10).toString()
|
||||||
|
Tokens.START.toString()
|
||||||
|
Tokens.END.toString()
|
||||||
|
Tokens.COLON.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,17 +10,6 @@ import java.util.HashMap
|
|||||||
|
|
||||||
class TokenizerTest extends TestUtils {
|
class TokenizerTest extends TestUtils {
|
||||||
|
|
||||||
def tokenTrue = Tokens.newBoolean(fakeOrigin(), true)
|
|
||||||
def tokenFalse = Tokens.newBoolean(fakeOrigin(), false)
|
|
||||||
def tokenNull = Tokens.newNull(fakeOrigin())
|
|
||||||
def tokenUnquoted(s: String) = Tokens.newUnquotedText(fakeOrigin(), s)
|
|
||||||
def tokenKeySubstitution(s: String) = Tokens.newSubstitution(fakeOrigin(), s, SubstitutionStyle.KEY)
|
|
||||||
def tokenPathSubstitution(s: String) = Tokens.newSubstitution(fakeOrigin(), s, SubstitutionStyle.PATH)
|
|
||||||
def tokenString(s: String) = Tokens.newString(fakeOrigin(), s)
|
|
||||||
def tokenDouble(d: Double) = Tokens.newDouble(fakeOrigin(), d)
|
|
||||||
def tokenInt(i: Int) = Tokens.newInt(fakeOrigin(), i)
|
|
||||||
def tokenLong(l: Long) = Tokens.newLong(fakeOrigin(), l)
|
|
||||||
|
|
||||||
def tokenize(origin: ConfigOrigin, input: Reader): java.util.Iterator[Token] = {
|
def tokenize(origin: ConfigOrigin, input: Reader): java.util.Iterator[Token] = {
|
||||||
Tokenizer.tokenize(origin, input)
|
Tokenizer.tokenize(origin, input)
|
||||||
}
|
}
|
||||||
@ -199,6 +188,7 @@ class TokenizerTest extends TestUtils {
|
|||||||
("1.2", 1.2),
|
("1.2", 1.2),
|
||||||
("1e6", 1e6),
|
("1e6", 1e6),
|
||||||
("1e-6", 1e-6),
|
("1e-6", 1e-6),
|
||||||
|
("1E-6", 1e-6), // capital E is allowed
|
||||||
("-1", -1),
|
("-1", -1),
|
||||||
("-1.2", -1.2))
|
("-1.2", -1.2))
|
||||||
|
|
||||||
|
@ -23,6 +23,18 @@ class UnitParserTest extends TestUtils {
|
|||||||
val result = Config.parseDuration(s, fakeOrigin(), "test")
|
val result = Config.parseDuration(s, fakeOrigin(), "test")
|
||||||
assertEquals(oneSecInNanos, result)
|
assertEquals(oneSecInNanos, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bad units
|
||||||
|
val e = intercept[ConfigException.BadValue] {
|
||||||
|
Config.parseDuration("100 dollars", fakeOrigin(), "test")
|
||||||
|
}
|
||||||
|
assertTrue(e.getMessage().contains("time unit"))
|
||||||
|
|
||||||
|
// bad number
|
||||||
|
val e2 = intercept[ConfigException.BadValue] {
|
||||||
|
Config.parseDuration("1 00 seconds", fakeOrigin(), "test")
|
||||||
|
}
|
||||||
|
assertTrue(e2.getMessage().contains("duration number"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -38,5 +50,17 @@ class UnitParserTest extends TestUtils {
|
|||||||
val result = Config.parseMemorySize(s, fakeOrigin(), "test")
|
val result = Config.parseMemorySize(s, fakeOrigin(), "test")
|
||||||
assertEquals(1024 * 1024, result)
|
assertEquals(1024 * 1024, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bad units
|
||||||
|
val e = intercept[ConfigException.BadValue] {
|
||||||
|
Config.parseMemorySize("100 dollars", fakeOrigin(), "test")
|
||||||
|
}
|
||||||
|
assertTrue(e.getMessage().contains("size unit"))
|
||||||
|
|
||||||
|
// bad number
|
||||||
|
val e2 = intercept[ConfigException.BadValue] {
|
||||||
|
Config.parseMemorySize("1 00 bytes", fakeOrigin(), "test")
|
||||||
|
}
|
||||||
|
assertTrue(e2.getMessage().contains("size number"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user