Repository: j-easy/easy-random
Issue: Fundamental issue in random-beans-validation

I found fundamental problem in `random-beans-validation` implementation. Currently every randomizer created by the package will generate same value every time (or almost every time, see below).

Let's for example look into [`SizeAnnotationHandler`](https://github.com/benas/random-beans/blob/master/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/SizeAnnotationHandler.java).

Every time randomizer is looked up for field annotated with `@Size` it will create [new instance](https://github.com/benas/random-beans/blob/master/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/SizeAnnotationHandler.java#L56) of `CharacterRandomizer` providing it with **same** seed and therefore randomizer will generate **same** value every time. Or almost every time, because randomizer for string length is [not using](https://github.com/benas/random-beans/blob/master/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/SizeAnnotationHandler.java#L54) seed (but it should). Other annotation handlers have same issue, except those generating constant values (for obvious reasons).

Ideal solution would be to somehow cache randomizers created for the field from annotation. But I'm not good with Java, so not sure how feasible/doable it is. 

Changed files:
random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PastAnnotationHandler.java
random-beans-validation/src/main/java/io/github/benas/randombeans/validation/MinMaxAnnotationHandler.java
random-beans-validation/src/main/java/io/github/benas/randombeans/validation/DecimaMinMaxAnnotationHandler.java
random-beans-validation/src/main/java/io/github/benas/randombeans/validation/SizeAnnotationHandler.java
random-beans-validation/src/test/java/io/github/benas/randombeans/validation/BeanValidationTest.java
random-beans-validation/src/main/java/io/github/benas/randombeans/validation/FutureAnnotationHandler.java
random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PatternAnnotationHandler.java

diff --git a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/DecimaMinMaxAnnotationHandler.java b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/DecimaMinMaxAnnotationHandler.java
index f23d2e86..155bb7f8 100644
--- a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/DecimaMinMaxAnnotationHandler.java
+++ b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/DecimaMinMaxAnnotationHandler.java
@@ -32,13 +32,14 @@ import javax.validation.constraints.DecimalMin;
 import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.util.Random;
 
 class DecimaMinMaxAnnotationHandler implements BeanValidationAnnotationHandler {
 
-    private long seed;
+    private final Random random;
 
     public DecimaMinMaxAnnotationHandler(long seed) {
-        this.seed = seed;
+        random = new Random(seed);
     }
 
     public Randomizer<?> getRandomizer(Field field) {
@@ -62,49 +63,49 @@ class DecimaMinMaxAnnotationHandler implements BeanValidationAnnotationHandler {
                 return new ByteRangeRandomizer(
                         minValue == null ? null : minValue.byteValue(),
                         maxValue == null ? null : maxValue.byteValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(Short.TYPE) || fieldType.equals(Short.class)) {
                 return new ShortRangeRandomizer(
                         minValue == null ? null : minValue.shortValue(),
                         maxValue == null ? null : maxValue.shortValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(Integer.TYPE) || fieldType.equals(Integer.class)) {
                 return new IntegerRangeRandomizer(
                         minValue == null ? null : minValue.intValue(),
                         maxValue == null ? null : maxValue.intValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(Long.TYPE) || fieldType.equals(Long.class)) {
                 return new LongRangeRandomizer(
                         minValue == null ? null : minValue.longValue(),
                         maxValue == null ? null : maxValue.longValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(BigInteger.class)) {
                 return new BigIntegerRangeRandomizer(
                         minValue == null ? null : minValue.intValue(),
                         maxValue == null ? null : maxValue.intValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(BigDecimal.class)) {
                 return new BigDecimalRangeRandomizer(
                         minValue == null ? null : minValue.longValue(),
                         maxValue == null ? null : maxValue.longValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(String.class)) {
                 BigDecimalRangeRandomizer delegate = new BigDecimalRangeRandomizer(
                         minValue == null ? null : minValue.longValue(),
                         maxValue == null ? null : maxValue.longValue(),
-                        seed
+                        random.nextLong()
                 );
                 return new StringDelegatingRandomizer(delegate);
             }
diff --git a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/FutureAnnotationHandler.java b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/FutureAnnotationHandler.java
index dc1f4b19..a7a6bddc 100644
--- a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/FutureAnnotationHandler.java
+++ b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/FutureAnnotationHandler.java
@@ -30,18 +30,19 @@ import io.github.benas.randombeans.util.Constants;
 import java.lang.reflect.Field;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.Random;
 
 class FutureAnnotationHandler implements BeanValidationAnnotationHandler {
 
-    private long seed;
+    private final Random random;
 
     public FutureAnnotationHandler(long seed) {
-        this.seed = seed;
+        random = new Random(seed);
     }
 
     public Randomizer<?> getRandomizer(Field field) {
         Calendar calendar = Calendar.getInstance();
         calendar.add(Calendar.YEAR, Constants.DEFAULT_DATE_RANGE);
-        return new DateRangeRandomizer(new Date(), calendar.getTime(), seed);
+        return new DateRangeRandomizer(new Date(), calendar.getTime(), random.nextLong());
     }
 }
diff --git a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/MinMaxAnnotationHandler.java b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/MinMaxAnnotationHandler.java
index c2792117..cbc21348 100644
--- a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/MinMaxAnnotationHandler.java
+++ b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/MinMaxAnnotationHandler.java
@@ -31,13 +31,14 @@ import javax.validation.constraints.Min;
 import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.util.Random;
 
 class MinMaxAnnotationHandler implements BeanValidationAnnotationHandler {
 
-    private long seed;
+    private final Random random;
 
     public MinMaxAnnotationHandler(long seed) {
-        this.seed = seed;
+        random = new Random(seed);
     }
 
     public Randomizer<?> getRandomizer(Field field) {
@@ -61,42 +62,42 @@ class MinMaxAnnotationHandler implements BeanValidationAnnotationHandler {
                 return new ByteRangeRandomizer(
                         minValue == null ? null : minValue.byteValue(),
                         maxValue == null ? null : maxValue.byteValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(Short.TYPE) || fieldType.equals(Short.class)) {
                 return new ShortRangeRandomizer(
                         minValue == null ? null : minValue.shortValue(),
                         maxValue == null ? null : maxValue.shortValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(Integer.TYPE) || fieldType.equals(Integer.class)) {
                 return new IntegerRangeRandomizer(
                         minValue == null ? null : minValue.intValue(),
                         maxValue == null ? null : maxValue.intValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(Long.TYPE) || fieldType.equals(Long.class)) {
                 return new LongRangeRandomizer(
                         minValue == null ? null : minValue,
                         maxValue == null ? null : maxValue,
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(BigInteger.class)) {
                 return new BigIntegerRangeRandomizer(
                         minValue == null ? null : minValue.intValue(),
                         maxValue == null ? null : maxValue.intValue(),
-                        seed
+                        random.nextLong()
                 );
             }
             if (fieldType.equals(BigDecimal.class)) {
                 return new BigDecimalRangeRandomizer(
                         minValue == null ? null : minValue,
                         maxValue == null ? null : maxValue,
-                        seed
+                        random.nextLong()
                 );
             }
         }
diff --git a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PastAnnotationHandler.java b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PastAnnotationHandler.java
index 16255216..b488272a 100644
--- a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PastAnnotationHandler.java
+++ b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PastAnnotationHandler.java
@@ -30,18 +30,19 @@ import io.github.benas.randombeans.util.Constants;
 import java.lang.reflect.Field;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.Random;
 
 class PastAnnotationHandler implements BeanValidationAnnotationHandler {
 
-    private long seed;
+    private final Random random;
 
     public PastAnnotationHandler(long seed) {
-        this.seed = seed;
+        random = new Random(seed);
     }
 
     public Randomizer<?> getRandomizer(Field field) {
         Calendar calendar = Calendar.getInstance();
         calendar.add(Calendar.YEAR, -Constants.DEFAULT_DATE_RANGE);
-        return new DateRangeRandomizer(calendar.getTime(), new Date(), seed);
+        return new DateRangeRandomizer(calendar.getTime(), new Date(), random.nextLong());
     }
 }
diff --git a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PatternAnnotationHandler.java b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PatternAnnotationHandler.java
index 4ae738e8..70810868 100644
--- a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PatternAnnotationHandler.java
+++ b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/PatternAnnotationHandler.java
@@ -28,13 +28,14 @@ import io.github.benas.randombeans.randomizers.RegularExpressionRandomizer;
 
 import javax.validation.constraints.Pattern;
 import java.lang.reflect.Field;
+import java.util.Random;
 
 class PatternAnnotationHandler implements BeanValidationAnnotationHandler {
 
-    private long seed;
+    private final Random random;
 
     public PatternAnnotationHandler(long seed) {
-        this.seed = seed;
+        random = new Random(seed);
     }
 
     public Randomizer<?> getRandomizer(Field field) {
@@ -43,7 +44,7 @@ class PatternAnnotationHandler implements BeanValidationAnnotationHandler {
 
         final String regex = patternAnnotation.regexp();
         if (fieldType.equals(String.class)) {
-            return new RegularExpressionRandomizer(regex, seed);
+            return new RegularExpressionRandomizer(regex, random.nextLong());
         }
         return null;
     }
diff --git a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/SizeAnnotationHandler.java b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/SizeAnnotationHandler.java
index d963ea6b..009bd5c1 100644
--- a/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/SizeAnnotationHandler.java
+++ b/random-beans-validation/src/main/java/io/github/benas/randombeans/validation/SizeAnnotationHandler.java
@@ -24,23 +24,21 @@
 package io.github.benas.randombeans.validation;
 
 import io.github.benas.randombeans.api.Randomizer;
-import io.github.benas.randombeans.randomizers.text.CharacterRandomizer;
+import io.github.benas.randombeans.randomizers.text.StringRandomizer;
 
 import javax.validation.constraints.Size;
 import java.lang.reflect.Field;
 import java.nio.charset.Charset;
-
-import static io.github.benas.randombeans.randomizers.range.IntegerRangeRandomizer.aNewIntegerRangeRandomizer;
-import static io.github.benas.randombeans.randomizers.text.CharacterRandomizer.aNewCharacterRandomizer;
+import java.util.Random;
 
 class SizeAnnotationHandler implements BeanValidationAnnotationHandler {
 
-    private long seed;
+    private final Random random;
 
     private Charset charset;
 
     public SizeAnnotationHandler(long seed, Charset charset) {
-        this.seed = seed;
+        random = new Random(seed);
         this.charset = charset;
     }
 
@@ -51,19 +49,7 @@ class SizeAnnotationHandler implements BeanValidationAnnotationHandler {
         final int min = sizeAnnotation.min();
         final int max = sizeAnnotation.max();
         if (fieldType.equals(String.class)) {
-            final int randomLength = aNewIntegerRangeRandomizer(min, max).getRandomValue();
-            return new Randomizer<String>() {
-                private final CharacterRandomizer characterRandomizer = aNewCharacterRandomizer(charset, seed);
-
-                @Override
-                public String getRandomValue() {
-                    StringBuilder stringBuilder = new StringBuilder();
-                    for (int i = 0; i < randomLength; i++) {
-                        stringBuilder.append(characterRandomizer.getRandomValue());
-                    }
-                    return stringBuilder.toString();
-                }
-            };
+            return new StringRandomizer(charset, min, max, random.nextLong());
         }
         return null;
     }
diff --git a/random-beans-validation/src/test/java/io/github/benas/randombeans/validation/BeanValidationTest.java b/random-beans-validation/src/test/java/io/github/benas/randombeans/validation/BeanValidationTest.java
index 934faaf9..2d4534b4 100644
--- a/random-beans-validation/src/test/java/io/github/benas/randombeans/validation/BeanValidationTest.java
+++ b/random-beans-validation/src/test/java/io/github/benas/randombeans/validation/BeanValidationTest.java
@@ -23,19 +23,22 @@
  */
 package io.github.benas.randombeans.validation;
 
-import io.github.benas.randombeans.api.EnhancedRandom;
-import org.junit.Before;
-import org.junit.Test;
+import static io.github.benas.randombeans.EnhancedRandomBuilder.aNewEnhancedRandom;
+import static io.github.benas.randombeans.EnhancedRandomBuilder.aNewEnhancedRandomBuilder;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.math.BigDecimal;
+import java.util.Set;
 
 import javax.validation.ConstraintViolation;
 import javax.validation.Validation;
 import javax.validation.Validator;
 import javax.validation.ValidatorFactory;
-import java.math.BigDecimal;
-import java.util.Set;
 
-import static io.github.benas.randombeans.EnhancedRandomBuilder.aNewEnhancedRandom;
-import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Before;
+import org.junit.Test;
+
+import io.github.benas.randombeans.api.EnhancedRandom;
 
 public class BeanValidationTest {
 
@@ -68,9 +71,9 @@ public class BeanValidationTest {
 
         assertThat(bean.getMinQuantity()).isGreaterThanOrEqualTo(5);// @Min(5) int minQuantity;
 
-        assertThat(bean.getMaxDiscount()).isLessThanOrEqualTo(new BigDecimal("30.00"));// @DecimalMax("30.00") BigDecimal maxDiscount;;
+        assertThat(bean.getMaxDiscount()).isLessThanOrEqualTo(new BigDecimal("30.00"));// @DecimalMax("30.00") BigDecimal maxDiscount;
 
-        assertThat(bean.getMinDiscount()).isGreaterThanOrEqualTo(new BigDecimal("5.00"));// @DecimalMin("5.00") BigDecimal minDiscount;;
+        assertThat(bean.getMinDiscount()).isGreaterThanOrEqualTo(new BigDecimal("5.00"));// @DecimalMin("5.00") BigDecimal minDiscount;
 
         assertThat(bean.getMinQuantity()).isGreaterThanOrEqualTo(5);// @Min(5) int minQuantity;
 
@@ -79,6 +82,26 @@ public class BeanValidationTest {
         assertThat(bean.getRegexString()).matches("[a-z]{4}");
     }
 
+    @Test
+    public void shouldGenerateTheSameValueForTheSameSeed() {
+        EnhancedRandom random = aNewEnhancedRandomBuilder().seed(123L).build();
+ 
+        BeanValidationAnnotatedBean bean = random.nextObject(BeanValidationAnnotatedBean.class);
+
+        assertThat(bean.getUsername()).isEqualTo("eOMtThyhVNLWUZNRcBaQKxIy");
+        // uses DateRange with now as end, so test is not repeatable
+        // assertThat(bean.getBirthday()).isEqualTo("2007-07-22T13:20:35.628");
+        // uses DateRange with now as start, so test is not repeatable
+        // assertThat(bean.getEventDate()).isEqualTo("2017-07-22T13:20:35.628");
+        assertThat(bean.getMaxQuantity()).isEqualTo(-2055951746);
+        assertThat(bean.getMinQuantity()).isEqualTo(91531906);
+        assertThat(bean.getMaxDiscount()).isEqualTo(new BigDecimal(30));
+        assertThat(bean.getMinDiscount()).isEqualTo(new BigDecimal(393126525614007301L));
+        assertThat(bean.getMinQuantity()).isEqualTo(91531906);
+        assertThat(bean.getBriefMessage()).isEqualTo("tguu");
+        assertThat(bean.getRegexString()).isEqualTo("vuna");
+    }
+
     @Test
     public void generatedBeanShouldBeValidUsingBeanValidationAPI() {
         BeanValidationAnnotatedBean bean = enhancedRandom.nextObject(BeanValidationAnnotatedBean.class);