Commit 36d36f97 authored by Matt Benson's avatar Matt Benson Committed by Phillip Webb

Improve RandomValuePropertySource

Rework RandomValuePropertySource class javadoc, particularly the
description of the `random.int` range suffix.

Also add support for range based random longs.

Fixes gh-3391
parent 35379f62
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -28,13 +28,31 @@ import org.springframework.util.StringUtils; ...@@ -28,13 +28,31 @@ import org.springframework.util.StringUtils;
/** /**
* {@link PropertySource} that returns a random value for any property that starts with * {@link PropertySource} that returns a random value for any property that starts with
* {@literal "random."}. Return a {@code byte[]} unless the property name ends with * {@literal "random."}. Where the "unqualified property name" is the portion of the
* {@literal ".int} or {@literal ".long"}. * requested property name beyond the "random." prefix, this {@link PropertySource}
* returns:
* <ul>
* <li>When {@literal "int"}, a random {@link Integer} value, restricted by an optionally
* specified range.</li>
* <li>When {@literal "long"}, a random {@link Long} value, restricted by an optionally
* specified range.</li>
* <li>Otherwise, a {@code byte[]}.</li>
* <ul>
* The {@literal "random.int"} and {@literal "random.long"} properties supports a range
* suffix whose syntax is:
* <p>
* {@code OPEN value (,max) CLOSE} where the {@code OPEN,CLOSE} are any character and
* {@code value,max} are integers. If {@code max} is provided then {@code value} is the
* minimum value and {@code max} is the maximum (exclusive).
* </p>
* *
* @author Dave Syer * @author Dave Syer
* @author Matt Benson
*/ */
public class RandomValuePropertySource extends PropertySource<Random> { public class RandomValuePropertySource extends PropertySource<Random> {
private static final String PREFIX = "random.";
private static Log logger = LogFactory.getLog(RandomValuePropertySource.class); private static Log logger = LogFactory.getLog(RandomValuePropertySource.class);
public RandomValuePropertySource(String name) { public RandomValuePropertySource(String name) {
...@@ -43,35 +61,66 @@ public class RandomValuePropertySource extends PropertySource<Random> { ...@@ -43,35 +61,66 @@ public class RandomValuePropertySource extends PropertySource<Random> {
@Override @Override
public Object getProperty(String name) { public Object getProperty(String name) {
if (!name.startsWith("random.")) { if (!name.startsWith(PREFIX)) {
return null; return null;
} }
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Generating random property for '" + name + "'"); logger.trace("Generating random property for '" + name + "'");
} }
if (name.endsWith("int")) { return getRandomValue(name.substring(PREFIX.length()));
}
private Object getRandomValue(String type) {
if (type.equals("int")) {
return getSource().nextInt(); return getSource().nextInt();
} }
if (name.startsWith("random.long")) { if (type.equals("long")) {
return getSource().nextLong(); return getSource().nextLong();
} }
if (name.startsWith("random.int") && name.length() > "random.int".length() + 1) { String range = getRange(type, "int");
String range = name.substring("random.int".length() + 1); if (range != null) {
range = range.substring(0, range.length() - 1); return getNextIntInRange(range);
return getNextInRange(range);
} }
byte[] bytes = new byte[32]; range = getRange(type, "long");
getSource().nextBytes(bytes); if (range != null) {
return DigestUtils.md5DigestAsHex(bytes); return getNextLongInRange(range);
}
return getRandomBytes();
}
private String getRange(String type, String prefix) {
if (type.startsWith(prefix)) {
int startIndex = prefix.length() + 1;
if (type.length() > startIndex) {
return type.substring(startIndex, type.length() - 1);
}
}
return null;
} }
private int getNextInRange(String range) { private int getNextIntInRange(String range) {
String[] tokens = StringUtils.commaDelimitedListToStringArray(range); String[] tokens = StringUtils.commaDelimitedListToStringArray(range);
Integer start = Integer.valueOf(tokens[0]); int start = Integer.parseInt(tokens[0]);
if (tokens.length == 1) { if (tokens.length == 1) {
return getSource().nextInt(start); return getSource().nextInt(start);
} }
return start + getSource().nextInt(Integer.valueOf(tokens[1]) - start); return start + getSource().nextInt(Integer.parseInt(tokens[1]) - start);
}
private long getNextLongInRange(String range) {
String[] tokens = StringUtils.commaDelimitedListToStringArray(range);
if (tokens.length == 1) {
return Math.abs(getSource().nextLong()) % Long.parseLong(tokens[0]);
}
long lowerBound = Long.parseLong(tokens[0]);
long upperBound = Long.parseLong(tokens[1]) - lowerBound;
return lowerBound + Math.abs(getSource().nextLong()) % upperBound;
}
private Object getRandomBytes() {
byte[] bytes = new byte[32];
getSource().nextBytes(bytes);
return DigestUtils.md5DigestAsHex(bytes);
} }
public static void addToEnvironment(ConfigurableEnvironment environment) { public static void addToEnvironment(ConfigurableEnvironment environment) {
......
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -26,6 +26,7 @@ import static org.junit.Assert.assertTrue; ...@@ -26,6 +26,7 @@ import static org.junit.Assert.assertTrue;
* Tests for {@link RandomValuePropertySource}. * Tests for {@link RandomValuePropertySource}.
* *
* @author Dave Syer * @author Dave Syer
* @author Matt Benson
*/ */
public class RandomValuePropertySourceTests { public class RandomValuePropertySourceTests {
...@@ -52,6 +53,7 @@ public class RandomValuePropertySourceTests { ...@@ -52,6 +53,7 @@ public class RandomValuePropertySourceTests {
Integer value = (Integer) this.source.getProperty("random.int[4,10]"); Integer value = (Integer) this.source.getProperty("random.int[4,10]");
assertNotNull(value); assertNotNull(value);
assertTrue(value >= 4); assertTrue(value >= 4);
assertTrue(value < 10);
} }
@Test @Test
...@@ -67,4 +69,18 @@ public class RandomValuePropertySourceTests { ...@@ -67,4 +69,18 @@ public class RandomValuePropertySourceTests {
assertNotNull(value); assertNotNull(value);
} }
@Test
public void longRange() {
Long value = (Long) this.source.getProperty("random.long[4,10]");
assertNotNull(value);
assertTrue(Long.toString(value), value >= 4L);
assertTrue(Long.toString(value), value < 10L);
}
@Test
public void longMax() {
Long value = (Long) this.source.getProperty("random.long(10)");
assertNotNull(value);
assertTrue(value < 10L);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment