From 57c68666fcb7e67c5fd73057dce752af0b37431a Mon Sep 17 00:00:00 2001 From: Anton Persson Date: Thu, 13 Sep 2018 13:58:51 +0200 Subject: [PATCH] Introduce nextValueOfTypes(Types... types) and excluding(Types... types) --- .../java/org/neo4j/test/rule/RandomRule.java | 2 +- .../neo4j/values/storable/RandomValues.java | 261 +++++++----------- .../values/storable/RandomValuesTest.java | 47 ++++ 3 files changed, 152 insertions(+), 158 deletions(-) diff --git a/community/random-values/src/main/java/org/neo4j/test/rule/RandomRule.java b/community/random-values/src/main/java/org/neo4j/test/rule/RandomRule.java index 781db2cee2e1d..8c8816c8855dc 100644 --- a/community/random-values/src/main/java/org/neo4j/test/rule/RandomRule.java +++ b/community/random-values/src/main/java/org/neo4j/test/rule/RandomRule.java @@ -283,7 +283,7 @@ public Value nextValue() public Value nextValue( RandomValues.Types type ) { - return randoms.nextValue( type ); + return randoms.nextValueOfType( type ); } // ============================ diff --git a/community/random-values/src/main/java/org/neo4j/values/storable/RandomValues.java b/community/random-values/src/main/java/org/neo4j/values/storable/RandomValues.java index ae18a61ef0a5f..eff6215f3bfe7 100644 --- a/community/random-values/src/main/java/org/neo4j/values/storable/RandomValues.java +++ b/community/random-values/src/main/java/org/neo4j/values/storable/RandomValues.java @@ -19,6 +19,9 @@ */ package org.neo4j.values.storable; +import org.apache.commons.lang3.ArrayUtils; +import org.eclipse.collections.impl.factory.Lists; + import java.lang.reflect.Array; import java.time.Duration; import java.time.Instant; @@ -30,6 +33,7 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Random; @@ -48,25 +52,6 @@ import static org.neo4j.values.storable.DurationValue.duration; import static org.neo4j.values.storable.LocalDateTimeValue.localDateTime; import static org.neo4j.values.storable.LocalTimeValue.localTime; -import static org.neo4j.values.storable.RandomValues.Types.BOOLEAN_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.BYTE_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.CARTESIAN_POINT_3D_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.CARTESIAN_POINT_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.DATE_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.DATE_TIME_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.DOUBLE_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.DURATION_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.FLOAT_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.GEOGRAPHIC_POINT_3D_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.GEOGRAPHIC_POINT_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.INT_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.LOCAL_DATE_TIME_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.LOCAL_TIME_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.LONG_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.PERIOD_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.SHORT_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.STRING_ARRAY; -import static org.neo4j.values.storable.RandomValues.Types.TIME_ARRAY; import static org.neo4j.values.storable.TimeValue.time; import static org.neo4j.values.storable.Values.byteValue; import static org.neo4j.values.storable.Values.doubleValue; @@ -77,55 +62,86 @@ /** * Helper class that generates generator values of all supported types. + *

+ * Value are generated in a pseudorandom fashion. + *

+ * The length of strings will be governed by {@link RandomValues.Configuration#stringMinLength()} and + * {@link RandomValues.Configuration#stringMaxLength()} and + * the length of arrays will be governed by {@link RandomValues.Configuration#arrayMinLength()} and + * {@link RandomValues.Configuration#arrayMaxLength()} + * unless method provide explicit arguments for those configurations in which case the provided argument will be used instead. */ public class RandomValues { public enum Types { - BOOLEAN( ValueGroup.NUMBER ), - BYTE( ValueGroup.NUMBER ), - SHORT( ValueGroup.NUMBER ), - INT( ValueGroup.NUMBER ), - LONG( ValueGroup.NUMBER ), - FLOAT( ValueGroup.NUMBER ), - DOUBLE( ValueGroup.NUMBER ), - STRING( ValueGroup.TEXT ), - LOCAL_DATE_TIME( ValueGroup.LOCAL_DATE_TIME ), - DATE( ValueGroup.DATE ), - LOCAL_TIME( ValueGroup.LOCAL_TIME ), - PERIOD( ValueGroup.DURATION ), - DURATION( ValueGroup.DURATION ), - TIME( ValueGroup.ZONED_TIME ), - DATE_TIME( ValueGroup.ZONED_DATE_TIME ), - CARTESIAN_POINT( ValueGroup.GEOMETRY ), - CARTESIAN_POINT_3D( ValueGroup.GEOMETRY ), - GEOGRAPHIC_POINT( ValueGroup.GEOMETRY ), - GEOGRAPHIC_POINT_3D( ValueGroup.GEOMETRY ), - BOOLEAN_ARRAY( ValueGroup.BOOLEAN_ARRAY ), - BYTE_ARRAY( ValueGroup.NUMBER_ARRAY ), - SHORT_ARRAY( ValueGroup.NUMBER_ARRAY ), - INT_ARRAY( ValueGroup.NUMBER_ARRAY ), - LONG_ARRAY( ValueGroup.NUMBER_ARRAY ), - FLOAT_ARRAY( ValueGroup.NUMBER_ARRAY ), - DOUBLE_ARRAY( ValueGroup.NUMBER_ARRAY ), - STRING_ARRAY( ValueGroup.TEXT_ARRAY ), - LOCAL_DATE_TIME_ARRAY( ValueGroup.LOCAL_DATE_TIME_ARRAY ), - DATE_ARRAY( ValueGroup.DATE_ARRAY ), - LOCAL_TIME_ARRAY( ValueGroup.LOCAL_TIME_ARRAY ), - PERIOD_ARRAY( ValueGroup.DURATION_ARRAY ), - DURATION_ARRAY( ValueGroup.DURATION_ARRAY ), - TIME_ARRAY( ValueGroup.ZONED_TIME_ARRAY ), - DATE_TIME_ARRAY( ValueGroup.ZONED_DATE_TIME_ARRAY ), - CARTESIAN_POINT_ARRAY( ValueGroup.GEOMETRY_ARRAY ), - CARTESIAN_POINT_3D_ARRAY( ValueGroup.GEOMETRY_ARRAY ), - GEOGRAPHIC_POINT_ARRAY( ValueGroup.GEOMETRY_ARRAY ), - GEOGRAPHIC_POINT_3D_ARRAY( ValueGroup.GEOMETRY_ARRAY ); + BOOLEAN( ValueGroup.NUMBER, BooleanValue.class ), + BYTE( ValueGroup.NUMBER, ByteValue.class ), + SHORT( ValueGroup.NUMBER, ShortValue.class ), + INT( ValueGroup.NUMBER, IntValue.class ), + LONG( ValueGroup.NUMBER, LongValue.class ), + FLOAT( ValueGroup.NUMBER, FloatValue.class ), + DOUBLE( ValueGroup.NUMBER, DoubleValue.class ), + STRING( ValueGroup.TEXT, UTF8StringValue.class ), + LOCAL_DATE_TIME( ValueGroup.LOCAL_DATE_TIME, LocalDateTimeValue.class ), + DATE( ValueGroup.DATE, DateValue.class ), + LOCAL_TIME( ValueGroup.LOCAL_TIME, LocalTimeValue.class ), + PERIOD( ValueGroup.DURATION, DurationValue.class ), + DURATION( ValueGroup.DURATION, DurationValue.class ), + TIME( ValueGroup.ZONED_TIME, TimeValue.class ), + DATE_TIME( ValueGroup.ZONED_DATE_TIME, DateTimeValue.class ), + CARTESIAN_POINT( ValueGroup.GEOMETRY, PointValue.class ), + CARTESIAN_POINT_3D( ValueGroup.GEOMETRY, PointValue.class ), + GEOGRAPHIC_POINT( ValueGroup.GEOMETRY, PointValue.class ), + GEOGRAPHIC_POINT_3D( ValueGroup.GEOMETRY, PointValue.class ), + BOOLEAN_ARRAY( ValueGroup.BOOLEAN_ARRAY, BooleanArray.class, true ), + BYTE_ARRAY( ValueGroup.NUMBER_ARRAY, ByteArray.class, true ), + SHORT_ARRAY( ValueGroup.NUMBER_ARRAY, ShortArray.class, true ), + INT_ARRAY( ValueGroup.NUMBER_ARRAY, IntArray.class, true ), + LONG_ARRAY( ValueGroup.NUMBER_ARRAY, LongArray.class, true ), + FLOAT_ARRAY( ValueGroup.NUMBER_ARRAY, FloatArray.class, true ), + DOUBLE_ARRAY( ValueGroup.NUMBER_ARRAY, DoubleArray.class, true ), + STRING_ARRAY( ValueGroup.TEXT_ARRAY, StringArray.class, true ), + LOCAL_DATE_TIME_ARRAY( ValueGroup.LOCAL_DATE_TIME_ARRAY, LocalDateTimeArray.class, true ), + DATE_ARRAY( ValueGroup.DATE_ARRAY, DateArray.class, true ), + LOCAL_TIME_ARRAY( ValueGroup.LOCAL_TIME_ARRAY, LocalTimeArray.class, true ), + PERIOD_ARRAY( ValueGroup.DURATION_ARRAY, DurationArray.class, true ), + DURATION_ARRAY( ValueGroup.DURATION_ARRAY, DurationArray.class, true ), + TIME_ARRAY( ValueGroup.ZONED_TIME_ARRAY, TimeArray.class, true ), + DATE_TIME_ARRAY( ValueGroup.ZONED_DATE_TIME_ARRAY, DateTimeArray.class, true ), + CARTESIAN_POINT_ARRAY( ValueGroup.GEOMETRY_ARRAY, PointArray.class, true ), + CARTESIAN_POINT_3D_ARRAY( ValueGroup.GEOMETRY_ARRAY, PointArray.class, true ), + GEOGRAPHIC_POINT_ARRAY( ValueGroup.GEOMETRY_ARRAY, PointArray.class, true ), + GEOGRAPHIC_POINT_3D_ARRAY( ValueGroup.GEOMETRY_ARRAY, PointArray.class, true ); public final ValueGroup valueGroup; + public final Class valueClass; + public final boolean arrayType; - Types( ValueGroup valueGroup ) + Types( ValueGroup valueGroup, Class valueClass ) + { + this( valueGroup, valueClass, false ); + } + + Types( ValueGroup valueGroup, Class valueClass, boolean arrayType ) { this.valueGroup = valueGroup; + this.valueClass = valueClass; + this.arrayType = arrayType; + } + + static Types[] arrayTypes() + { + return Lists.mutable.of( Types.values() ) + .select( t -> t.arrayType ) + .toArray( new Types[0] ); + } + + static Types[] nonArrayTypes() + { + return Lists.mutable.of( Types.values() ) + .select( t -> !t.arrayType ) + .toArray( new Types[0] ); } } @@ -177,26 +193,9 @@ public int maxCodePoint() public static final int MAX_BASIC_MULTILINGUAL_PLANE_CODE_POINT = 0xFFFF; public static final Configuration DEFAULT_CONFIGURATION = new Default(); - private static final Types[] TYPES = Types.values(); - private static final Types[] ARRAY_TYPES = new Types[]{BOOLEAN_ARRAY, - BYTE_ARRAY, - SHORT_ARRAY, - INT_ARRAY, - LONG_ARRAY, - FLOAT_ARRAY, - DOUBLE_ARRAY, - STRING_ARRAY, - LOCAL_DATE_TIME_ARRAY, - DATE_ARRAY, - LOCAL_TIME_ARRAY, - PERIOD_ARRAY, - DURATION_ARRAY, - TIME_ARRAY, - DATE_TIME_ARRAY, - CARTESIAN_POINT_ARRAY, - CARTESIAN_POINT_3D_ARRAY, - GEOGRAPHIC_POINT_ARRAY, - GEOGRAPHIC_POINT_3D_ARRAY}; + private static final Types[] ALL_TYPES = Types.values(); + private static final Types[] ARRAY_TYPES = Types.arrayTypes(); + private static final Types[] NON_ARRAY_TYPES = Types.nonArrayTypes(); private static final long NANOS_PER_SECOND = 1_000_000_000L; private final Generator generator; @@ -285,20 +284,22 @@ public static RandomValues create( SplittableRandom random ) */ public Value nextValue() { - return nextValue( nextType() ); + return nextValueOfType( among( ALL_TYPES ) ); } - /** - * Returns the next pseudorandom {@link Value} of given type - *

- * The length of strings will be governed by {@link Configuration#stringMinLength()} and - * {@link Configuration#stringMaxLength()} and - * the length of arrays will be governed by {@link Configuration#arrayMinLength()} and - * {@link Configuration#arrayMaxLength()} - * - * @return the next pseudorandom {@link Value} of given type - */ - public Value nextValue( Types type ) + public Value nextValueOfTypes( Types... types ) + { + return nextValueOfType( among( types ) ); + } + + public Types[] excluding( Types... types ) + { + return Lists.mutable.of( Types.values() ) + .withoutAll( Arrays.asList( types ) ) + .toArray( new Types[Types.values().length - types.length] ); + } + + public Value nextValueOfType( Types type ) { switch ( type ) { @@ -393,63 +394,7 @@ public Value nextValue( Types type ) */ public ArrayValue nextArray() { - return nextArray( configuration.arrayMinLength(), configuration.arrayMaxLength() ); - } - - /** - * Returns the next pseudorandom {@link ArrayValue}, distributed uniformly among the supported Value types where - * the length of the array is given by the provided values. - * - * @param minLength the minimum length of the array - * @param maxLength the maximum length of the array - * @return the next pseudorandom {@link ArrayValue} - */ - private ArrayValue nextArray( int minLength, int maxLength ) - { - Types type = nextType( ARRAY_TYPES ); - switch ( type ) - { - case BOOLEAN_ARRAY: - return nextBooleanArray( minLength, maxLength ); - case BYTE_ARRAY: - return nextByteArray( minLength, maxLength ); - case SHORT_ARRAY: - return nextShortArray( minLength, maxLength ); - case STRING_ARRAY: - return nextStringArray( minLength, maxLength ); - case INT_ARRAY: - return nextIntArray( minLength, maxLength ); - case LONG_ARRAY: - return nextLongArray( minLength, maxLength ); - case FLOAT_ARRAY: - return nextFloatArray( minLength, maxLength ); - case DOUBLE_ARRAY: - return nextDoubleArray( minLength, maxLength ); - case LOCAL_DATE_TIME_ARRAY: - return nextLocalDateTimeArray( minLength, maxLength ); - case DATE_ARRAY: - return nextDateArray( minLength, maxLength ); - case LOCAL_TIME_ARRAY: - return nextLocalTimeArray( minLength, maxLength ); - case PERIOD_ARRAY: - return nextPeriodArray( minLength, maxLength ); - case DURATION_ARRAY: - return nextDurationArray( minLength, maxLength ); - case TIME_ARRAY: - return nextTimeArray( minLength, maxLength ); - case DATE_TIME_ARRAY: - return nextDateTimeArray( minLength, maxLength ); - case CARTESIAN_POINT_ARRAY: - return nextCartesianPointArray( minLength, maxLength ); - case CARTESIAN_POINT_3D_ARRAY: - return nextCartesian3DPointArray( minLength, maxLength ); - case GEOGRAPHIC_POINT_ARRAY: - return nextGeographicPointArray( minLength, maxLength ); - case GEOGRAPHIC_POINT_3D_ARRAY: - return nextGeographic3DPointArray( minLength, maxLength ); - default: - throw new IllegalArgumentException( "Not array type: " + type ); - } + return (ArrayValue) nextValueOfType( among( ARRAY_TYPES ) ); } /** @@ -1852,6 +1797,18 @@ public T among( T[] among ) return among[generator.nextInt( among.length )]; } + public T[] among( Class clazz, T[] among, int numberOfElements ) + { + if ( numberOfElements < 0 || numberOfElements > among.length ) + { + throw new IllegalArgumentException( "Can select " + numberOfElements + " from array with " + among.length + " elements." ); + } + ArrayUtils.shuffle( among ); + T[] result = (T[]) Array.newInstance( clazz, numberOfElements ); + System.arraycopy( among, 0, result, 0, numberOfElements ); + return result; + } + /** * Returns a random element of the provided list * @@ -1922,16 +1879,6 @@ private static boolean contains( T[] array, T contains ) return false; } - private Types nextType() - { - return nextType( TYPES ); - } - - private Types nextType( Types[] types ) - { - return among( types ); - } - private static int nextPowerOf2( int i ) { return 1 << (32 - Integer.numberOfLeadingZeros( i )); diff --git a/community/random-values/src/test/java/org/neo4j/values/storable/RandomValuesTest.java b/community/random-values/src/test/java/org/neo4j/values/storable/RandomValuesTest.java index 8d71ea6d12c7a..e49ed0b55338a 100644 --- a/community/random-values/src/test/java/org/neo4j/values/storable/RandomValuesTest.java +++ b/community/random-values/src/test/java/org/neo4j/values/storable/RandomValuesTest.java @@ -19,6 +19,7 @@ */ package org.neo4j.values.storable; +import org.apache.commons.lang3.ArrayUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -277,6 +278,40 @@ public void nextValue() assertThat( seen, empty() ); } + @Test + public void nextValueOfTypes() + { + RandomValues.Types[] allTypes = RandomValues.Types.values(); + RandomValues.Types[] including = randomValues.among( RandomValues.Types.class, allTypes, randomValues.nextInt( allTypes.length ) + 1 ); + HashSet> seen = new HashSet<>(); + for ( RandomValues.Types type : including ) + { + seen.add( type.valueClass ); + } + for ( int i = 0; i < ITERATIONS; i++ ) + { + Value value = randomValues.nextValueOfTypes( including ); + assertValueAmongTypes( including, value ); + markSeen( value.getClass(), seen ); + } + assertThat( seen, empty() ); + } + + @Test + public void excluding() + { + RandomValues.Types[] allTypes = RandomValues.Types.values(); + RandomValues.Types[] excluding = randomValues.among( RandomValues.Types.class, allTypes, randomValues.nextInt( allTypes.length ) + 1 ); + RandomValues.Types[] including = randomValues.excluding( excluding ); + for ( RandomValues.Types excludedType : excluding ) + { + if ( ArrayUtils.contains( including, excludedType ) ) + { + fail( "Including array " + Arrays.toString( including ) + " contains excluded type " + excludedType ); + } + } + } + @Test public void nextBasicMultilingualPlaneTextValue() { @@ -289,6 +324,18 @@ public void nextBasicMultilingualPlaneTextValue() } } + private void assertValueAmongTypes( RandomValues.Types[] types, Value value ) + { + for ( RandomValues.Types type : types ) + { + if ( type.valueClass.isAssignableFrom( value.getClass() ) ) + { + return; + } + } + fail( "Value " + value + " was not among types " + Arrays.toString( types ) ); + } + private void assertKnownType( Class typeToCheck, Set> types ) { for ( Class type : types )