Skip to content

Commit

Permalink
make GeneratorDecorator only decorate Generators
Browse files Browse the repository at this point in the history
  • Loading branch information
Bijnagte committed Jul 13, 2016
1 parent ba6ea6e commit 6af3ff6
Show file tree
Hide file tree
Showing 18 changed files with 209 additions and 121 deletions.
5 changes: 3 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ buildscript {
}

dependencies {
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7'
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.3'
classpath 'org.ajoberstar:gradle-git:1.4.2'
}
Expand All @@ -31,6 +31,7 @@ dependencies {
compile('org.codehaus.groovy:groovy-all:2.4.7')
compile('org.spockframework:spock-core:1.0-groovy-2.4')
compile('com.github.mifmif:generex:1.0.1')
testCompile('cglib:cglib-nodep:3.2.4')
}

asciidoctor {
Expand Down Expand Up @@ -107,7 +108,7 @@ bintray {
repo = 'nagternal'
name = 'spock-genesis'
desc = 'Mostly lazy data generators for property based testing using the Spock test framework'
websiteUrl = 'https://github.com/Bijnagte/spock-genesis'
websiteUrl = 'https://bijnagte.github.io/spock-genesis'
issueTrackerUrl = 'https://github.com/Bijnagte/spock-genesis/issues'
vcsUrl = 'https://github.com/Bijnagte/spock-genesis.git'
licenses = ['MIT']
Expand Down
14 changes: 2 additions & 12 deletions src/main/groovy/spock/genesis/Gen.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package spock.genesis
import spock.genesis.generators.CyclicGenerator
import spock.genesis.generators.FactoryGenerator
import spock.genesis.generators.Generator
import spock.genesis.generators.IterableGenerator
import spock.genesis.generators.composites.DefinedMapGenerator
import spock.genesis.generators.composites.ListGenerator
import spock.genesis.generators.composites.PojoGenerator
Expand Down Expand Up @@ -225,17 +226,6 @@ class Gen {
new RandomElementGenerator(source.toList())
}

/**
* Produces a lazy infinite {@link CyclicGenerator} that repeats
* an {@link Iterator}.
*
* @param source {@link Iterator} to repeat over
* @return an instance of {@link CyclicGenerator}
*/
static CyclicGenerator cycle(Iterator source) {
new CyclicGenerator(source)
}

/**
* Produces a lazy infinite {@link CyclicGenerator} that repeats
* an {@link Iterable}.
Expand All @@ -244,7 +234,7 @@ class Gen {
* @return an instance of {@link CyclicGenerator}
*/
static <T> CyclicGenerator cycle(Iterable<T> source) {
new CyclicGenerator<T>(source)
new IterableGenerator<T>(source).repeat()
}

/**
Expand Down
21 changes: 14 additions & 7 deletions src/main/groovy/spock/genesis/extension/ExtensionMethods.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ package spock.genesis.extension

import groovy.transform.CompileStatic
import spock.genesis.generators.Generator
import spock.genesis.generators.GeneratorDecorator
import spock.genesis.generators.LimitedGenerator
import spock.genesis.generators.IterableGenerator
import spock.genesis.generators.ObjectIteratorGenerator

@CompileStatic
Expand All @@ -18,15 +17,23 @@ class ExtensionMethods {
}

static <T> Generator<T> toGenerator(Iterable<T> self, boolean finite = false) {
new GeneratorDecorator<T>(self, finite)
new IterableGenerator<T>(self, finite)
}

static Generator<String> toGenerator(String self) {
new ObjectIteratorGenerator<String>(self)
}

static Generator<Object> toGenerator(Object self) {
new ObjectIteratorGenerator(self)
}

static <K,V> Generator<Map.Entry<K,V>> toGenerator(Map<K,V> self) {
new ObjectIteratorGenerator(self)
}

static <T> Generator<T> toGenerator(Collection<T> self) {
new LimitedGenerator<T>(self)
new IterableGenerator<T>(self)
}

static Generator toGenerator(Class clazz) {
Expand All @@ -38,11 +45,11 @@ class ExtensionMethods {
}

static <T> Generator<T> toGenerator(T... self) {
new LimitedGenerator<T>(self)
new IterableGenerator<T>(self)
}

static Generator toGenerator(Iterator self) {
new GeneratorDecorator(new Iterable() {
static <T> Generator<T> toGenerator(Iterator<T> self) {
new IterableGenerator<T>(new Iterable () {
@Override
Iterator iterator() {
self
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package spock.genesis.generators

import groovy.transform.CompileStatic
import spock.genesis.extension.ExtensionMethods

/**
* A lazy infinite generator that repeats an iterator.
Expand All @@ -15,7 +16,7 @@ class CyclicGenerator<E> extends GeneratorDecorator<E> {
}

CyclicGenerator(Iterable<E> iterable) {
super(iterable, GeneratorUtils.isFinite(iterable) || !Generator.isInstance(iterable))
super(ExtensionMethods.toGenerator(iterable))
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package spock.genesis.generators

import groovy.transform.CompileStatic
import spock.genesis.extension.ExtensionMethods

/**
* A lazy generator that returns the next value from the wrapped iterator that satisfies a predicate Closure.
Expand All @@ -12,7 +13,7 @@ class FilteredGenerator<E> extends GeneratorDecorator<E> {
private final Closure predicate

FilteredGenerator(Iterable<E> iterable, Closure predicate) {
super(iterable)
super(ExtensionMethods.toGenerator(iterable))
this.predicate = predicate
}

Expand Down
13 changes: 9 additions & 4 deletions src/main/groovy/spock/genesis/generators/Generator.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import spock.genesis.generators.values.NullGenerator
* An Iterator that generates a type (usually lazily)
* @param < E > the generated type
*/
@SuppressWarnings('SpaceAroundMapEntryColon')
@CompileStatic
abstract class Generator<E> implements Iterable<E> {
abstract class Generator<E> implements Iterable<E>, Closeable {

/**
* Wraps this generator in a generator that returns values that matches the supplied predicate
Expand Down Expand Up @@ -40,7 +39,7 @@ abstract class Generator<E> implements Iterable<E> {
Iterable<E>[] all = new Iterable<E>[iterables.length + 1]
all[0] = this
for (int i = 0; i < iterables.length; i++) {
all[i +1] = iterables[i]
all[i + 1] = iterables[i]

}
new SequentialMultisourceGenerator<E>(all)
Expand All @@ -66,6 +65,7 @@ abstract class Generator<E> implements Iterable<E> {
* @param resultsPerNull the average number of results from this generator per null result
* @return {@link spock.genesis.generators.MultiSourceGenerator}
*/
@SuppressWarnings('SpaceAroundMapEntryColon')
MultiSourceGenerator<E> withNulls(int resultsPerNull) {
Map weightedGenerators = [(this): resultsPerNull, (new NullGenerator<E>()): 1]
new MultiSourceGenerator<E>(weightedGenerators)
Expand All @@ -85,9 +85,14 @@ abstract class Generator<E> implements Iterable<E> {
* If false then the generator may still terminate when iterated
* @return true if the Generator will terminate
*/
abstract boolean isFinite()
boolean isFinite() {
!iterator().hasNext()
}

List<E> getRealized() {
this.collect().asList()
}

@SuppressWarnings('EmptyMethodInAbstractClass')
void close() { }
}
22 changes: 9 additions & 13 deletions src/main/groovy/spock/genesis/generators/GeneratorDecorator.groovy
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
package spock.genesis.generators

import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic

/**
* A generator that wraps an iterator to provide {@link Generator} methods.
* Base {@link Generator} class for functionality that modifies a stream from the wrapped generator.
* @param < E > the generated type
*/
@CompileStatic
class GeneratorDecorator<E> extends Generator<E> implements Closeable {
protected Iterable<E> generator
protected Generator<E> generator
final boolean finiteOverride

GeneratorDecorator(Iterable<E> iterable) {
this.generator = iterable
this.finiteOverride = false
GeneratorDecorator(Generator<E> generator) {
this.generator = generator
this.finiteOverride = generator.finite
}

GeneratorDecorator(Iterable<E> iterable, boolean finite) {
this.generator = iterable
GeneratorDecorator(Generator<E> generator, boolean finite) {
this.generator = generator
this.finiteOverride = finite
}

Expand All @@ -41,13 +40,10 @@ class GeneratorDecorator<E> extends Generator<E> implements Closeable {

@Override
boolean isFinite() {
finiteOverride || GeneratorUtils.isFinite(generator)
finiteOverride || !generator.iterator().hasNext()
}

@CompileDynamic
void close() {
if (generator.respondsTo('close')) {
generator.close()
}
generator.close()
}
}
59 changes: 59 additions & 0 deletions src/main/groovy/spock/genesis/generators/IterableGenerator.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package spock.genesis.generators

import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic

/**
* A generator that wraps an iterator to provide {@link Generator} methods.
* @param < E > the generated type
*/
@CompileStatic
class IterableGenerator<E> extends Generator<E> implements Closeable {

private final Iterable<E> iterable
private final boolean finite

IterableGenerator(Iterable<E> iterable) {
this.iterable = iterable
this.finite = Generator.isInstance(iterable) ? false : true
}

IterableGenerator(Iterable<E> iterable, boolean finite) {
this.iterable = iterable
this.finite = finite
}

IterableGenerator(E... array) {
this.iterable = Arrays.asList(array)
this.finite = true
}

@Override
UnmodifiableIterator<E> iterator() {
new UnmodifiableIterator<E>() {
private final Iterator<E> iterator = iterable.iterator()
@Override
boolean hasNext() {
iterator.hasNext()
}

@Override
E next() {
iterator.next()
}
}
}

@Override
boolean isFinite() {
finite || GeneratorUtils.isFinite(iterable)
}

@CompileDynamic
@Override
void close() {
if (iterable.respondsTo('close')) {
iterable.close()
}
}
}
13 changes: 2 additions & 11 deletions src/main/groovy/spock/genesis/generators/LimitedGenerator.groovy
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
package spock.genesis.generators

import groovy.transform.CompileStatic
import spock.genesis.extension.ExtensionMethods

@CompileStatic
class LimitedGenerator<E> extends GeneratorDecorator<E> {

final int iterationLimit

LimitedGenerator(Iterable<E> iterable, int iterationLimit) {
super(iterable)
super(ExtensionMethods.toGenerator(iterable))
this.iterationLimit = iterationLimit
}

LimitedGenerator(Collection<E> collection) {
super(collection)
this.iterationLimit = collection.size()
}

LimitedGenerator(E... array) {
super(Arrays.asList(array))
this.iterationLimit = array.length
}

UnmodifiableIterator<E> iterator() {
final Iterator ITERATOR = super.iterator()
new UnmodifiableIterator<E>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package spock.genesis.generators

import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import spock.genesis.extension.ExtensionMethods

/**
* A generator that returns the next value from one of its source generators at random.
Expand All @@ -10,24 +10,25 @@ import groovy.transform.CompileStatic
@CompileStatic
class MultiSourceGenerator<E> extends Generator<E> implements Closeable {

final List<Iterable<E>> iterables
private final List<Generator<E>> generators
final Random random = new Random()

MultiSourceGenerator(Collection<Iterable<E>> iterables) {
this.iterables = iterables.toList()
this.generators = iterables.collect { ExtensionMethods.toGenerator(it) }
}

MultiSourceGenerator(Map<Iterable<E>, Integer> weightedIterators) {
List i = []
weightedIterators.each { iterable, qty ->
qty.times { i << iterable }
def generator = ExtensionMethods.toGenerator(iterable)
qty.times { i << generator }
}
this.iterables = i
this.generators = i
}

UnmodifiableIterator<E> iterator() {
new UnmodifiableIterator<E>() {
private final List<Iterator<E>> iterators = iterables*.iterator()
private final List<UnmodifiableIterator<E>> iterators = generators*.iterator()
/**
* @return true if any of the source generators has next
*/
Expand Down Expand Up @@ -65,22 +66,15 @@ class MultiSourceGenerator<E> extends Generator<E> implements Closeable {
* @param additional
* @return a new MultiSourceGenerator
*/
MultiSourceGenerator plus(Collection<Iterable<E>> additional) {
new MultiSourceGenerator(additional + iterables)
MultiSourceGenerator<E> plus(Collection<Iterable<E>> additional) {
new MultiSourceGenerator(additional + generators)
}

void close() {
iterables.each { close(it) }
}

@CompileDynamic
void close(Iterable generator) {
if (generator.respondsTo('close')) {
generator.close()
}
generators.each { it.close() }
}

boolean isFinite() {
GeneratorUtils.allFinite(iterables)
generators.every { it.finite }
}
}
Loading

0 comments on commit 6af3ff6

Please sign in to comment.