Xeger can cause stackoverflow and doesn't understand \d \w \s

fixes gh-589, fixes gh-590
This commit is contained in:
Marcin Grzejszczak
2018-03-21 10:53:30 +01:00
parent f57cefc68a
commit cd07f6d42b
2 changed files with 61 additions and 6 deletions

View File

@@ -31,11 +31,16 @@ import dk.brics.automaton.Transition;
/**
* An object that will generate text from a regular expression. In a way, it's the opposite of a regular expression
* matcher: an instance of this class will produce text that is guaranteed to match the regular expression passed in.
*
* slight modifications by Marcin Grzejszczak
*/
public class Xeger {
private final Automaton automaton;
private Random random;
// Added by Marcin Grzejszczak
// may lead to stackoverflow when regex is unbounded
static int ITERATION_LIMIT = 200;
/**
* Constructs a new instance, accepting the regular expression and the randomizer.
@@ -47,7 +52,12 @@ public class Xeger {
public Xeger(String regex, Random random) {
assert regex != null;
assert random != null;
this.automaton = new RegExp(regex).toAutomaton();
// https://stackoverflow.com/questions/1578789/how-do-i-generate-text-matching-a-regular-expression-from-a-regular-expression
String pattern = regex
.replace("\\d", "[0-9]") // Used d=Digit
.replace("\\w", "[A-Za-z0-9_]") // Used =Word
.replace("\\s", "[ \t\r\n]"); // Used s="White"Space
this.automaton = new RegExp(pattern).toAutomaton();
this.random = random;
}
@@ -67,11 +77,15 @@ public class Xeger {
*/
public String generate() {
StringBuilder builder = new StringBuilder();
generate(builder, this.automaton.getInitialState());
int counter = 0;
generate(builder, this.automaton.getInitialState(), counter);
return builder.toString();
}
private void generate(StringBuilder builder, State state) {
private void generate(StringBuilder builder, State state, int counter) {
if (counter >= ITERATION_LIMIT) {
return;
}
List<Transition> transitions = state.getSortedTransitions(false);
if (transitions.size() == 0) {
assert state.isAccept();
@@ -85,7 +99,7 @@ public class Xeger {
// Moving on to next transition
Transition transition = transitions.get(option - (state.isAccept() ? 1 : 0));
appendChoice(builder, transition);
generate(builder, transition.getDest());
generate(builder, transition.getDest(), ++counter);
}
private void appendChoice(StringBuilder builder, Transition transition) {

View File

@@ -17,12 +17,12 @@
*/
package repackaged.nl.flotsam.xeger;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -38,6 +38,47 @@ public class XegerTest {
}
}
@Test
public void shouldNotGenerateMoreThanTheLimit() {
String regex = "[ab]{5}";
Xeger.ITERATION_LIMIT = 1;
Xeger generator = new Xeger(regex);
for (int i = 0; i < 100; i++) {
String text = generator.generate();
assertTrue(text.length() == 1);
}
}
@Test
public void shouldGenerateTextCorrectlyForDigitSign() {
String regex = "\\d+";
Xeger generator = new Xeger(regex);
for (int i = 0; i < 100; i++) {
String text = generator.generate();
assertTrue(text.matches(regex));
}
}
@Test
public void shouldGenerateTextCorrectlyForWordSign() {
String regex = "\\w+";
Xeger generator = new Xeger(regex);
for (int i = 0; i < 100; i++) {
String text = generator.generate();
assertTrue(text.matches(regex));
}
}
@Test
public void shouldGenerateTextCorrectlyForWhiteSpaceSign() {
String regex = "\\s+";
Xeger generator = new Xeger(regex);
for (int i = 0; i < 100; i++) {
String text = generator.generate();
assertTrue(text.matches(regex));
}
}
@Test
public void testRepeatableRegex() {
for (int x = 0; x < 1000; x++) {