Skip to content

Commit

Permalink
Merge pull request #95 from curious-odd-man/#88.InvestigateTmpComplet…
Browse files Browse the repository at this point in the history
…eTests_2

#88. Investigating TmpComple test patterns
  • Loading branch information
curious-odd-man authored Feb 24, 2024
2 parents 9eb48f4 + db3413b commit a6b003f
Show file tree
Hide file tree
Showing 89 changed files with 10,361 additions and 143 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ public class Main {
| `[...]` | Single character from ones that are inside brackets. `[a-zA-Z]` (dash) also supported |
| `[^...]` | Single character except the ones in brackets. `[^a]` - any symbol except 'a' |
| `()` | To group multiple characters for the repetitions |
| `foo(?=bar)` and `(?<=foo)bar` | Positive lookahead and lookbehind. These are equivalent to `foobar` |
| `foo(?!bar)` and `(?<!foo)bar` | Negative lookahead and lookbehind. |
| `foo(?=bar)` and `(?<=foo)bar` | Limited support. Positive lookahead and lookbehind. These are equivalent to `foobar`. Please see `Lookahead and Lookbehind` section. |
| `foo(?!bar)` and `(?<!foo)bar` | Limited support. Negative lookahead and lookbehind. Please see `Lookahead and Lookbehind` section. |
| <code>(a&#124;b)</code> | Alternatives |
| \\ | Escape character (use \\\\ (double backslash) to generate single \ character) |

Expand Down
20 changes: 10 additions & 10 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@
<min.maven.version>3.6.1</min.maven.version>

<!-- Dependencies versions -->
<junit.jupiter.version>5.10.0-M1</junit.jupiter.version>
<junit.platform.version>1.10.0-M1</junit.platform.version>
<jmh.version>1.36</jmh.version>
<junit.jupiter.version>5.10.1</junit.jupiter.version>
<junit.platform.version>1.10.1</junit.platform.version>
<jmh.version>1.37</jmh.version>

<!-- Plugins versions -->
<maven.compiler.plugin.version>3.11.0</maven.compiler.plugin.version>
<maven.surefire.plugin.version>3.1.0</maven.surefire.plugin.version>
<maven.sources.plugin.version>3.2.1</maven.sources.plugin.version>
<maven.javadoc.plugin.version>3.5.0</maven.javadoc.plugin.version>
<maven.compiler.plugin.version>3.12.1</maven.compiler.plugin.version>
<maven.surefire.plugin.version>3.2.5</maven.surefire.plugin.version>
<maven.sources.plugin.version>3.3.0</maven.sources.plugin.version>
<maven.javadoc.plugin.version>3.6.3</maven.javadoc.plugin.version>
<maven.gpg.plugin.version>3.1.0</maven.gpg.plugin.version>
<maven.enforcer.plugin.version>3.3.0</maven.enforcer.plugin.version>
<maven.enforcer.plugin.version>3.4.1</maven.enforcer.plugin.version>
<nexus.staging.maven.plugin.version>1.6.13</nexus.staging.maven.plugin.version>
<maven.versions.plugin.version>2.15.0</maven.versions.plugin.version>
<maven.versions.plugin.version>2.16.2</maven.versions.plugin.version>

<jacoco.maven.plugin.version>0.8.10</jacoco.maven.plugin.version>
<jacoco.maven.plugin.version>0.8.11</jacoco.maven.plugin.version>
</properties>

<licenses>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,11 @@ public void visit(Choice node) {
int i = aRandom.nextInt(nodes.length);
nodes[i].visit(this);
// To match group values along with generated values - we need to prepend groups values before the generated
} while (pattern.matcher(valuePrefixBuilder + aStringBuilder.substring(pos))
.matches());
} while (pattern.matcher(valuePrefixBuilder + aStringBuilder.substring(pos)).matches());
}

/**
* We need to add existing group values, so that we could later use it in a matching pattern
* Need to add existing group values, so that we could later use it in a matching pattern
*
* @param groupsBuilder
* @param valuePrefixBuilder
Expand Down
13 changes: 10 additions & 3 deletions src/test/java/com/github/curiousoddman/rgxgen/CompleteTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import org.junit.jupiter.params.provider.MethodSource;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -61,9 +63,11 @@ public static Stream<Arguments> getData() {
{"Periodic Table Elements", Boolean.FALSE, "\\b(?:A[cglmr-u]|B[aehikr]?|C[adefl-orsu]?|D[bsy]|E[rsu]|F[elmr]?|G[ade]|H[efgos]?|I[nr]?|Kr?|L[airuv]|M[dgont]|N[abdeiop]?|Os?|P[abdmortu]?|R[abe-hnu]|S[bcegimnr]?|T[abcehilm]|U(?:u[opst])?|V|W|Xe|Yb?|Z[nr])\\b"},
{"Russia Phone Number", Boolean.FALSE, "^((\\+7|7|8)+([0-9]){10})$|\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b"},
{"Brainfuck code", Boolean.FALSE, "^[+-<>.,\\[\\] \t\n\r]+$"},
{"USA/Canada Zip codes", Boolean.FALSE, "(^\\d{5}(-\\d{4})?$)|(^[A-Z]{1}\\d{1}[A-Z]{1} *\\d{1}[A-Z]{1}\\d{1}$)"},
{"USA and Canada Zip codes", Boolean.FALSE, "(^\\d{5}(-\\d{4})?$)|(^[A-Z]{1}\\d{1}[A-Z]{1} *\\d{1}[A-Z]{1}\\d{1}$)"},
{"JS comments", Boolean.TRUE, "//(?![\\S]{2,}\\.[\\w]).*|/\\*(.|\n)+\\*/"},
{"2-5 letter palindromes", Boolean.FALSE, "\\b(\\w?)(\\w)\\w?\\2\\1"},
{"Morse code", Boolean.TRUE, "^[.-]{1,5}(?: +[.-]{1,5})*(?: +[.-]{1,5}(?: +[.-]{1,5})*)$"},
{"JWT", Boolean.TRUE, "^[A-Za-z0-9-_=]+\\.[A-Za-z0-9-_=]+\\.?[A-Za-z0-9-_.+/=]*$"},
})
.flatMap(arr -> IntStream.range(0, 100)
.mapToObj(index -> Arguments.of(arr[0], arr[1], arr[2], index)));
Expand All @@ -86,8 +90,11 @@ public void generateNotMatchingTest(String aName, boolean aUseFind, String aRege
assertFalse(matches(aRegex, s, aUseFind), "Text: '" + s + "'does not match pattern " + aRegex);
}

private boolean matches(String aRegex, String text, boolean aUseFind) {
Matcher matcher = Pattern.compile(aRegex).matcher(text);
private static Map<String, Pattern> PATTERN_CACHE = new HashMap<>();

private static boolean matches(String pattern, String text, boolean aUseFind) {
Pattern compiledPattern = PATTERN_CACHE.computeIfAbsent(pattern, k -> Pattern.compile(pattern));
Matcher matcher = compiledPattern.matcher(text);
return aUseFind ? matcher.find() : matcher.matches();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
import com.github.curiousoddman.rgxgen.config.RgxGenProperties;
import com.github.curiousoddman.rgxgen.data.TestPattern;
import com.github.curiousoddman.rgxgen.data.TestPatternCaseInsensitive;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.IOException;
Expand All @@ -15,13 +18,14 @@
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.github.curiousoddman.rgxgen.testutil.TestingUtilities.newRandom;
import static java.util.Collections.singletonList;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assumptions.assumeFalse;

/**
* The aim of these test is to show that the result of generation was changed by writing values generated with same seed into a text file.
Expand All @@ -45,12 +49,40 @@ public static Stream<TestPattern> getPatterns() {
return Arrays.stream(TestPattern.values());
}

public static Stream<Arguments> getCompleteTestsPatterns() {
return CompleteTests.getData();
}

@BeforeAll
static void cleanupFiles() throws IOException {
try (Stream<Path> pathStream = Files.walk(caseInsensitivePath)) {
pathStream.filter(Files::isRegularFile)
.forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}

try (Stream<Path> pathStream = Files.walk(caseSensitivePath)) {
pathStream.filter(Files::isRegularFile)
.forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}

@ParameterizedTest
@MethodSource("getCaseInsensitivePatterns")
void verifyThatAllCaseInsensitivePatternsStaysTheSameTest(TestPatternCaseInsensitive data) throws IOException {
String name = data.name();
Path fileName = caseInsensitivePath.resolve("matching").resolve(name + ".txt").toAbsolutePath();
Files.deleteIfExists(fileName);
Path fileName = caseInsensitivePath.resolve("matching").resolve(createFileName(name)).toAbsolutePath();
RgxGenProperties properties = new RgxGenProperties();
RgxGenOption.CASE_INSENSITIVE.setInProperties(properties, true);
RgxGen rgxGen = RgxGen.parse(properties, data.getPattern());
Expand All @@ -65,8 +97,7 @@ void verifyThatAllCaseInsensitivePatternsStaysTheSameTest(TestPatternCaseInsensi
@MethodSource("getCaseInsensitivePatterns")
void verifyThatAllCaseInsensitivePatternsStaysTheSameNotMatchingTest(TestPatternCaseInsensitive data) throws IOException {
String name = data.name();
Path fileName = caseInsensitivePath.resolve("notmatching").resolve(name + ".txt").toAbsolutePath();
Files.deleteIfExists(fileName);
Path fileName = caseInsensitivePath.resolve("notmatching").resolve(createFileName(name)).toAbsolutePath();
RgxGenProperties properties = new RgxGenProperties();
RgxGenOption.CASE_INSENSITIVE.setInProperties(properties, true);
RgxGen rgxGen = RgxGen.parse(properties, data.getPattern());
Expand All @@ -81,8 +112,7 @@ void verifyThatAllCaseInsensitivePatternsStaysTheSameNotMatchingTest(TestPattern
@MethodSource("getPatterns")
void verifyThatAllCaseSensitivePatternsStaysTheSameTest(TestPattern data) throws IOException {
String name = data.name();
Path fileName = caseSensitivePath.resolve("matching").resolve(name + ".txt").toAbsolutePath();
Files.deleteIfExists(fileName);
Path fileName = caseSensitivePath.resolve("matching").resolve(createFileName(name)).toAbsolutePath();
RgxGen rgxGen = RgxGen.parse(data.getPattern());
Random random = newRandom(17);
for (int i = 0; i < NUM_ITERATIONS; i++) {
Expand All @@ -96,8 +126,7 @@ void verifyThatAllCaseSensitivePatternsStaysTheSameTest(TestPattern data) throws
@MethodSource("getPatterns")
void verifyThatAllCaseSensitivePatternsStaysTheSameNotMatchingTest(TestPattern data) throws IOException {
String name = data.name();
Path fileName = caseSensitivePath.resolve("notmatching").resolve(name + ".txt").toAbsolutePath();
Files.deleteIfExists(fileName);
Path fileName = caseSensitivePath.resolve("notmatching").resolve(createFileName(name)).toAbsolutePath();
RgxGen rgxGen = RgxGen.parse(data.getPattern());
Random random = newRandom(17);
for (int i = 0; i < NUM_ITERATIONS; i++) {
Expand All @@ -112,4 +141,51 @@ void verifyThatAllCaseSensitivePatternsStaysTheSameNotMatchingTest(TestPattern d
}
}
}

@Test
void verifyCompletePatternsHaveDifferentNames() {
assertDoesNotThrow(() -> {
getCompleteTestsPatterns()
.filter(arguments -> (int) arguments.get()[3] == 0) // Seed is 0
.map(arguments -> arguments.get()[0])
.map(Object::toString)
.collect(Collectors.toMap(Function.identity(), Function.identity()));
});
}

@ParameterizedTest(name = "{index}: {0}")
@MethodSource("getCompleteTestsPatterns")
public void generateTest(String name, boolean aUseFind, String aRegex, int aSeed) throws IOException {
Path fileName = caseSensitivePath.resolve("matching").resolve(createFileName(name)).toAbsolutePath();
RgxGen rgxGen = RgxGen.parse(aRegex);
String generated = rgxGen.generate(newRandom(aSeed));
try {
Files.write(fileName, singletonList(generated), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
} catch (MalformedInputException e) {
System.out.println("Failed to write a text " + e.getMessage());
System.out.println(generated.chars().mapToObj(String::valueOf).collect(Collectors.toList()));
System.out.println(generated);
fail(e);
}
}

@ParameterizedTest(name = "{index}: {0}")
@MethodSource("getCompleteTestsPatterns")
public void generateNotMatchingTest(String name, boolean aUseFind, String aRegex, int aSeed) throws IOException {
Path fileName = caseSensitivePath.resolve("notmatching").resolve(createFileName(name)).toAbsolutePath();
RgxGen rgxGen = RgxGen.parse(aRegex);
String generated = rgxGen.generateNotMatching(newRandom(aSeed));
try {
Files.write(fileName, singletonList(generated), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
} catch (MalformedInputException e) {
System.out.println("Failed to write a text " + e.getMessage());
System.out.println(generated.chars().mapToObj(String::valueOf).collect(Collectors.toList()));
System.out.println(generated);
fail(e);
}
}

private static String createFileName(String name) {
return name.replace('/', '_').replace('\\', '_') + ".txt";
}
}
34 changes: 18 additions & 16 deletions src/test/java/com/github/curiousoddman/rgxgen/TmpCompleteTests.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.github.curiousoddman.rgxgen;

import com.github.curiousoddman.rgxgen.testutil.TestingUtilities;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
Expand All @@ -12,6 +11,7 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static com.github.curiousoddman.rgxgen.testutil.TestingUtilities.newRandom;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand All @@ -21,15 +21,20 @@ public class TmpCompleteTests {

public static Stream<Arguments> getTestData() {
return Arrays.stream(new Object[][]{
//{"Test", Boolean.FALSE, "a\nb"}
{"Morse code", Boolean.TRUE, "^[.-]{1,5}(?:[ \t]+[.-]{1,5})*(?:[ \t]+[.-]{1,5}(?:[ \t]+[.-]{1,5})*)*$"}, // Very slow not matching generation. + java.regex fails to parse..
// {"Morse code", Boolean.TRUE, "^[.-]{1,5}(?: +[.-]{1,5})*(?: +[.-]{1,5}(?: +[.-]{1,5})*)*$"}, // Very slow not matching generation. + java.regex fails to parse..
// {"Domain name", Boolean.TRUE, "(?!w{1,}\\.)(\\w+\\.?)([a-zA-Z]+)(\\.\\w+)"}, // Fails infrequently...
// {"ISO-8601 Date", Boolean.TRUE, "^(?![+-]?\\d{4,5}-?(?:\\d{2}|W\\d{2})T)(?:|(\\d{4}|[+-]\\d{5})-?(?:|(0\\d|1[0-2])(?:|-?([0-2]\\d|3[0-1]))|([0-2]\\d{2}|3[0-5]\\d|36[0-6])|W([0-4]\\d|5[0-3])(?:|-?([1-7])))(?:(?!\\d)|T(?=\\d)))(?:|([01]\\d|2[0-4])(?:|:?([0-5]\\d)(?:|:?([0-5]\\d)(?:|\\.(\\d{3})))(?:|[zZ]|([+-](?:[01]\\d|2[0-4]))(?:|:?([0-5]\\d)))))$"}, // Something totally wrong
// {"Unix Path", Boolean.TRUE, "/|((?=/)|\\.|\\.\\.|~|~(?=/))(/(?=[^/])[^/]+)*/?"}, // Not matching generation fails
// {"Hashtags", Boolean.TRUE, "\\B#([a-z0-9]{2,})(?![~!@#$%^&*()=+_`\\-\\|\\/'\\[\\]\\{\\}]|[?.,]*\\w)"}, // This partially fails
// {"HTML Tags", Boolean.FALSE, "(<script(\\s|\\S)*?</script>)|(<style(\\s|\\S)*?</style>)|(<!--(\\s|\\S)*?-->)|(</?(\\s|\\S)*?>)"}, // This hangs
// {"JWT", Boolean.TRUE,"^[A-Za-z0-9-_=]+\\.[A-Za-z0-9-_=]+\\.?[A-Za-z0-9-_.+/=]*$"}, // Parsing failure in handleRange()!!
// Fails infrequently...
{"Domain name", Boolean.TRUE, "(?!w{1,}\\.)(\\w+\\.?)([a-zA-Z]+)(\\.\\w+)"},

// Negative lookahead
// {"ISO-8601 Date", Boolean.TRUE, "^(?![+-]?\\d{4,5}-?(?:\\d{2}|W\\d{2})T)(?:|(\\d{4}|[+-]\\d{5})-?(?:|(0\\d|1[0-2])(?:|-?([0-2]\\d|3[0-1]))|([0-2]\\d{2}|3[0-5]\\d|36[0-6])|W([0-4]\\d|5[0-3])(?:|-?([1-7])))(?:(?!\\d)|T(?=\\d)))(?:|([01]\\d|2[0-4])(?:|:?([0-5]\\d)(?:|:?([0-5]\\d)(?:|\\.(\\d{3})))(?:|[zZ]|([+-](?:[01]\\d|2[0-4]))(?:|:?([0-5]\\d)))))$"},

// Not matching generation fails
{"Unix Path", Boolean.TRUE, "/|((?=/)|\\.|\\.\\.|~|~(?=/))(/(?=[^/])[^/]+)*/?"},

// This partially fails
{"Hashtags", Boolean.TRUE, "\\B#([a-z0-9]{2,})(?![~!@#$%^&*()=+_`\\-\\|\\/'\\[\\]\\{\\}]|[?.,]*\\w)"},

// This hangs
// {"HTML Tags", Boolean.FALSE, "(<script(\\s|\\S)*?</script>)|(<style(\\s|\\S)*?</style>)|(<!--(\\s|\\S)*?-->)|(</?(\\s|\\S)*?>)"},
})
.flatMap(arr -> IntStream.range(0, 100)
.mapToObj(index -> Arguments.of(arr[0], arr[1], arr[2], index)));
Expand All @@ -39,23 +44,20 @@ public static Stream<Arguments> getTestData() {
@MethodSource("getTestData")
public void generateTest(String name, boolean useFind, String pattern, int seed) {
RgxGen rgxGen = RgxGen.parse(pattern);
String s = rgxGen.generate(TestingUtilities.newRandom(seed));
System.out.println("Matching: '" + s + "'");
String s = rgxGen.generate(newRandom(seed));
assertTrue(matches(s, pattern, useFind), "Text: '" + s + "' does not match pattern " + pattern);
}

@ParameterizedTest(name = "{index}: {0}")
@MethodSource("getTestData")
public void generateNotMatchingTest(String name, boolean useFind, String pattern, int seed) {
RgxGen rgxGen = RgxGen.parse(pattern);
String s = rgxGen.generateNotMatching(TestingUtilities.newRandom(seed));
System.out.println("Not Matching: '" + s + "'");
String s = rgxGen.generateNotMatching(newRandom(seed));
assertFalse(matches(s, pattern, useFind), "Text: '" + s + "' does not match pattern " + pattern);
}

private static boolean matches(String text, String pattern, boolean useFind) {
Matcher matcher = Pattern.compile(pattern)
.matcher(text);
Matcher matcher = Pattern.compile(pattern).matcher(text);
return useFind ? matcher.find() : matcher.matches();
}
}
Loading

0 comments on commit a6b003f

Please sign in to comment.