Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added charArrays which avoid boxing, and added base32 and geohash encoding #56

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion core/src/main/java/org/quicktheories/generators/Generate.java
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,45 @@ public static Gen<Character> characters(int startInclusive, int endInclusive) {
return CodePoints.codePoints(startInclusive, endInclusive, '!')
.map(l -> (char) l.intValue());
}


/**
* One dimensional char arrays
* @param sizes Gen of sizes for the arrays
* @param contents Gen of contents
* @return A Gen of char[]
*/
public static Gen<char[]> charArrays(Gen<Integer> sizes, Gen<Character> contents) {
Gen<char[]> gen = td -> {
int size = sizes.generate(td);
char[] is = new char[size];
for (int i = 0; i != size; i++) {
is[i] = contents.generate(td);
}
return is;
};
return gen.describedAs(Arrays::toString);
}

/**
* One dimensional char arrays
* @param sizes Gen of sizes for the arrays
* @param domain of content
* @return A Gen of char[]
*/
public static Gen<char[]> charArrays(Gen<Integer> sizes, char[] domain) {
Constraint constraints = Constraint.between(0, domain.length - 1).withNoShrinkPoint();
Gen<char[]> gen = td -> {
int size = sizes.generate(td);
char[] is = new char[size];
for (int i = 0; i != size; i++) {
int idx = (int) td.next(constraints);
is[i] = domain[idx];
}
return is;
};
return gen.describedAs(Arrays::toString);
}

/**
* One dimensional int arrays
* @param sizes Gen of sizes for the arrays
Expand Down
69 changes: 69 additions & 0 deletions core/src/main/java/org/quicktheories/generators/StringsDSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ public class StringsDSL {
private static final int BASIC_LATIN_FIRST_CODEPOINT = 0x0020;
private static final int ASCII_LAST_CODEPOINT = 0x007F;
private static final int LARGEST_DEFINED_BMP_CODEPOINT = 65533;
// https://en.wikipedia.org/wiki/Geohash
private static final char[] GEOHASH = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'b', 'c', 'd', 'e', 'f', 'g',
'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
};
// https://tools.ietf.org/html/rfc4648
private static final char[] BASE_32 = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7'
};

/**
* Generates integers as Strings, and shrinks towards "0".
Expand Down Expand Up @@ -94,6 +104,26 @@ public StringGeneratorBuilder betweenCodePoints(int minInclusive, int maxInclusi
return new StringGeneratorBuilder(minInclusive,
maxInclusive);
}

/**
* Constructs a EncodingStringGeneratorBuilder which will build Strings composed from
* base32 encoding
*
* @return a EncodingStringGeneratorBuilder
*/
public EncodingStringGeneratorBuilder base32() {
return new EncodingStringGeneratorBuilder(BASE_32);
}

/**
* Constructs a EncodingStringGeneratorBuilder which will build Strings composed from
* geohash encoding
*
* @return a EncodingStringGeneratorBuilder
*/
public EncodingStringGeneratorBuilder geohash() {
return new EncodingStringGeneratorBuilder(GEOHASH);
}

public static class StringGeneratorBuilder {

Expand Down Expand Up @@ -154,4 +184,43 @@ public Gen<String> ofLengthBetween(int minLength, int maxLength) {

}

public static class EncodingStringGeneratorBuilder {
private final char[] domain;

private EncodingStringGeneratorBuilder(char[] domain) {
this.domain = domain;
}

/**
* Generates Strings of a fixed length.
*
* @param fixedLength
* - the fixed length for the Strings
* @return a Source of type String
*/
public Gen<String> ofLength(int fixedLength) {
return ofLengthBetween(fixedLength, fixedLength);
}

/**
* Generates Strings of length bounded between minLength and maxLength
* inclusively.
*
* @param minLength
* - minimum inclusive length of String
* @param maxLength
* - maximum inclusive length of String
* @return a Source of type String
*/
public Gen<String> ofLengthBetween(int minLength, int maxLength) {
ArgumentAssertions.checkArguments(minLength <= maxLength,
"The minLength (%s) is longer than the maxLength(%s)",
minLength, maxLength);
ArgumentAssertions.checkArguments(minLength >= 0,
"The length of a String cannot be negative; %s is not an accepted argument",
minLength);
return Generate.charArrays(Generate.range(minLength, maxLength), domain).map(a -> new String(a));
}
}

}
19 changes: 18 additions & 1 deletion core/src/test/java/org/quicktheories/generators/ArraysTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.quicktheories.core.Gen;
import org.quicktheories.generators.Generate;

import java.util.Arrays;

public class ArraysTest {

private static final int ASCII_LAST_CODEPOINT = 0x007F;
Expand All @@ -31,7 +33,22 @@ public void shrinksTowardsEmptyArrayWhenZeroLengthsAllowed() {
assertThatGenerator(testee).shrinksTowards(new Character[0]);
}


@Test
public void shouldGenerateAllPossibleCharArraysWithinSmallDomain() {
Gen<char[]> testee = Generate.charArrays(Generate.range(1, 2), Generate.pick(Arrays.asList('a', 'b')));
assertThatGenerator(testee).generatesAllOf(
new char[] { 'a', 'a' }, new char[] { 'a', 'b' }, new char[] { 'b', 'a' },
new char[] { 'b', 'b' });
}

@Test
public void shouldGenerateAllPossibleCharArraysWithinSmallDomainNoBoxing() {
Gen<char[]> testee = Generate.charArrays(Generate.range(1, 2), new char[] {'a', 'b'});
assertThatGenerator(testee).generatesAllOf(
new char[] { 'a', 'a' }, new char[] { 'a', 'b' }, new char[] { 'b', 'a' },
new char[] { 'b', 'b' });
}

@Test
public void shrinksTowardsSmallestAllowedArrayWithSmallestContents() {
Gen<Integer[]> testee = Generate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,44 @@ public void fixedLengthStringsAreFixedLength() {
.forAll(strings().allPossible().ofLength(100))
.check(s -> s.length() == 100);
}


@Test
public void boundedLengthBase32() {
Gen<String> testee = strings().base32().ofLengthBetween(3, 200);
qt()
.withExamples(100000)
.forAll(testee)
.check( s -> s.length() <= 200 && s.length() >= 3 && isValidBase32(s));
}

@Test
public void boundedLengthGeohash() {
Gen<String> testee = strings().geohash().ofLengthBetween(3, 200);
qt()
.withExamples(100000)
.forAll(testee)
.check( s -> s.length() <= 200 && s.length() >= 3 && isValidGeohash(s));
}

private static boolean isValidBase32(final String str) {
for (int i = 0, len = str.length(); i < len; i++) {
char c = str.charAt(i);
if (!(('A' <= c && c <= 'Z') || ('2' <= c & c <= '7'))) {
return false;
}
}
return true;
}

private static boolean isValidGeohash(final String str) {
for (int i = 0, len = str.length(); i < len; i++) {
char c = str.charAt(i);
// geohash has gaps in the alphabet, see https://en.wikipedia.org/wiki/Geohash for range
if (!(('0' <= c & c <= '9') || ('b' <= c && c <= 'h') || ('j' <= c && c <= 'k') || ('m' <= c && c <= 'n') || ('p' <= c && c <= 'z'))) {
return false;
}
}
return true;
}

}