001/*
002 * Copyright 2023 the original author or authors.
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * https://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.test.generator;
017
018import static de.cuioss.tools.collect.CollectionLiterals.mutableList;
019import static java.util.Objects.requireNonNull;
020
021import java.io.Serializable;
022import java.net.URL;
023import java.time.LocalDate;
024import java.time.LocalDateTime;
025import java.time.LocalTime;
026import java.time.ZoneId;
027import java.time.ZoneOffset;
028import java.time.ZonedDateTime;
029import java.time.temporal.Temporal;
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.Collection;
033import java.util.Date;
034import java.util.List;
035import java.util.Locale;
036import java.util.Optional;
037import java.util.TimeZone;
038import java.util.stream.Collectors;
039
040import de.cuioss.test.generator.impl.CollectionGenerator;
041import de.cuioss.test.generator.impl.DecoratorGenerator;
042import de.cuioss.test.generator.impl.FloatObjectGenerator;
043import de.cuioss.test.generator.impl.LocalDateGenerator;
044import de.cuioss.test.generator.impl.LocalDateTimeGenerator;
045import de.cuioss.test.generator.impl.LocalTimeGenerator;
046import de.cuioss.test.generator.impl.NonBlankStringGenerator;
047import de.cuioss.test.generator.impl.NumberGenerator;
048import de.cuioss.test.generator.impl.ShortObjectGenerator;
049import de.cuioss.test.generator.impl.URLGenerator;
050import de.cuioss.test.generator.impl.ZoneOffsetGenerator;
051import de.cuioss.test.generator.impl.ZonedDateTimeGenerator;
052import de.cuioss.test.generator.internal.net.QuickCheckGeneratorAdapter;
053import de.cuioss.test.generator.internal.net.java.quickcheck.Generator;
054import de.cuioss.test.generator.internal.net.java.quickcheck.generator.CombinedGenerators;
055import de.cuioss.test.generator.internal.net.java.quickcheck.generator.PrimitiveGenerators;
056import de.cuioss.test.generator.internal.net.java.quickcheck.generator.support.FixedValuesGenerator;
057import lombok.experimental.UtilityClass;
058
059/**
060 * Provides a number of {@link TypedGenerator} for arbitrary java-types
061 *
062 * @author Oliver Wolff
063 */
064@UtilityClass
065public class Generators {
066
067    /**
068     * Factory method for creating a generator for a possible given enum.
069     *
070     * @param type to be checked must represent an enum
071     * @return an {@link Optional} on the corresponding {@link TypedGenerator} if
072     *         the given type is an enum can be found, {@link Optional#empty()}
073     *         otherwise
074     */
075    @SuppressWarnings({ "rawtypes", "unchecked" })
076    public static <T> Optional<TypedGenerator<T>> enumValuesIfAvailable(final Class<T> type) {
077        if (null == type || !type.isEnum()) {
078            return Optional.empty();
079        }
080        return Optional.of(new QuickCheckGeneratorAdapter(type, PrimitiveGenerators.enumValues((Class<Enum>) type)));
081    }
082
083    /**
084     * Factory method for creating a generator for a given enum.
085     *
086     * @param type to be checked must represent an enum
087     * @return A {@link TypedGenerator} for the given enmu
088     */
089    public static <T extends Enum<T>> TypedGenerator<T> enumValues(final Class<T> type) {
090        requireNonNull(type);
091        return new QuickCheckGeneratorAdapter<>(type, PrimitiveGenerators.enumValues(type));
092    }
093
094    /**
095     * Factory method for creating a {@link TypedGenerator} for non-empty Strings.
096     *
097     * @return a {@link TypedGenerator} for non-empty Strings
098     */
099    public static TypedGenerator<String> nonEmptyStrings() {
100        return new QuickCheckGeneratorAdapter<>(String.class, PrimitiveGenerators.nonEmptyStrings());
101    }
102
103    /**
104     * Factory method for creating a {@link TypedGenerator} for non-blank Strings.
105     *
106     * @return a {@link TypedGenerator} for non-blank Strings.
107     */
108    public static TypedGenerator<String> nonBlankStrings() {
109        return new NonBlankStringGenerator();
110    }
111
112    /**
113     * Factory method for creating a {@link TypedGenerator} for Strings.
114     *
115     * @param minSize lower bound of size
116     * @param maxSize upper bound of size
117     * @return a {@link TypedGenerator} for Strings
118     */
119    public static TypedGenerator<String> strings(final int minSize, final int maxSize) {
120        return new QuickCheckGeneratorAdapter<>(String.class, PrimitiveGenerators.strings(minSize, maxSize));
121    }
122
123    /**
124     * Factory method for creating strings with given characters and size.
125     *
126     * @param chars   to be generated
127     * @param minSize lower bound of size
128     * @param maxSize upper bound of size
129     * @return a {@link TypedGenerator} for Strings
130     */
131    public static TypedGenerator<String> strings(final String chars, final int minSize, final int maxSize) {
132        return new QuickCheckGeneratorAdapter<>(String.class, PrimitiveGenerators.strings(chars, minSize, maxSize));
133    }
134
135    /**
136     * Factory method for creating a {@link TypedGenerator} for any Strings, may be
137     * null or empty.
138     *
139     * @return a {@link TypedGenerator} for Strings
140     */
141    public static TypedGenerator<String> strings() {
142        return new QuickCheckGeneratorAdapter<>(String.class, PrimitiveGenerators.strings());
143    }
144
145    /**
146     * Factory method for creating a {@link TypedGenerator} for letter Strings.
147     *
148     * @param minSize lower bound of size
149     * @param maxSize upper bound of size
150     * @return a {@link TypedGenerator} for Strings
151     */
152    public static TypedGenerator<String> letterStrings(final int minSize, final int maxSize) {
153        return new QuickCheckGeneratorAdapter<>(String.class, PrimitiveGenerators.letterStrings(minSize, maxSize));
154    }
155
156    /**
157     * Factory method for creating a {@link TypedGenerator} for sensible / simple
158     * non empty letter Strings. The mininmal size is 3, the maximal size between 3
159     * and 256 characters
160     *
161     * @return a {@link TypedGenerator} for Strings
162     */
163    public static TypedGenerator<String> letterStrings() {
164        return letterStrings(3, integers(3, 256).next());
165    }
166
167    /**
168     * Factory method for creating a {@link TypedGenerator} for a number of fixed
169     * values.
170     *
171     * @param type   of the value
172     * @param values to be generated from.
173     * @return a {@link TypedGenerator} for the given values
174     */
175    @SafeVarargs
176    public static <T> TypedGenerator<T> fixedValues(final Class<T> type, final T... values) {
177        return fixedValues(type, Arrays.asList(values));
178    }
179
180    /**
181     * Factory method for creating a {@link TypedGenerator} for a number of fixed
182     * values.
183     *
184     * @param values to be generated from.
185     * @return a {@link TypedGenerator} for the given values
186     */
187    @SafeVarargs
188    public static <T> TypedGenerator<T> fixedValues(final T... values) {
189        return fixedValues(Arrays.asList(values));
190    }
191
192    /**
193     * Factory method for creating a {@link TypedGenerator} for a number of fixed
194     * values.
195     *
196     * @param type   of the value
197     * @param values to be generated from.
198     * @return a {@link TypedGenerator} for the given values
199     */
200    public static <T> TypedGenerator<T> fixedValues(final Class<T> type, final Iterable<T> values) {
201        return new QuickCheckGeneratorAdapter<>(type, new FixedValuesGenerator<>(mutableList(values)));
202    }
203
204    /**
205     * Factory method for creating a {@link TypedGenerator} for a number of fixed
206     * values.
207     *
208     * @param values to be generated from.
209     * @return a {@link TypedGenerator} for the given values
210     */
211    public static <T> TypedGenerator<T> fixedValues(final Iterable<T> values) {
212        return fixedValues(determineSupertypeFromIterable(values), values);
213    }
214
215    /* Combined generators */
216
217    /**
218     * Factory method for creating a {@link TypedGenerator} generating unique
219     * values. In case this does not work it will throw an {@link RuntimeException}
220     *
221     * @param source to be generated from.
222     * @return a {@link TypedGenerator} for the given values
223     */
224    @SuppressWarnings("unchecked") // Check not needed, because given TypedGenerator provides
225    // correct type
226    public static <T> TypedGenerator<T> uniqueValues(final TypedGenerator<T> source) {
227        return new QuickCheckGeneratorAdapter<>((Class<T>) source.getClass(),
228                CombinedGenerators.uniqueValues(unwrap(source)));
229    }
230
231    /**
232     * Factory method for creating a {@link CollectionGenerator} generating
233     * {@link Collection}s from the given {@link TypedGenerator} .
234     *
235     * @param source to be generated from.
236     * @return a {@link TypedGenerator} for the given values
237     */
238    public static <T> CollectionGenerator<T> asCollectionGenerator(final TypedGenerator<T> source) {
239        return new CollectionGenerator<>(source);
240    }
241
242    // Basic Java Types
243
244    /**
245     * Factory method for creating a {@link TypedGenerator} for boolean primitives.
246     *
247     * @return a {@link TypedGenerator} for boolean primitives
248     */
249    public static TypedGenerator<Boolean> booleans() {
250        return new QuickCheckGeneratorAdapter<>(boolean.class, PrimitiveGenerators.booleans());
251    }
252
253    /**
254     * Factory method for creating a {@link TypedGenerator} for {@link Boolean}.
255     *
256     * @return a {@link TypedGenerator} for {@link Boolean}
257     */
258    public static TypedGenerator<Boolean> booleanObjects() {
259        return new QuickCheckGeneratorAdapter<>(Boolean.class, PrimitiveGenerators.booleans());
260    }
261
262    /**
263     * Factory method for creating a {@link TypedGenerator} for byte primitives.
264     *
265     * @return a {@link TypedGenerator} for byte primitives
266     */
267    public static TypedGenerator<Byte> bytes() {
268        return new QuickCheckGeneratorAdapter<>(byte.class, PrimitiveGenerators.bytes());
269    }
270
271    /**
272     * Factory method for creating a {@link TypedGenerator} for {@link Byte}.
273     *
274     * @return a {@link TypedGenerator} for {@link Byte}
275     */
276    public static TypedGenerator<Byte> byteObjects() {
277        return new QuickCheckGeneratorAdapter<>(Byte.class, PrimitiveGenerators.bytes());
278    }
279
280    /**
281     * Factory method for creating a {@link TypedGenerator} for char primitives.
282     *
283     * @return a {@link TypedGenerator} for char primitives
284     */
285    public static TypedGenerator<Character> characters() {
286        return new QuickCheckGeneratorAdapter<>(char.class, PrimitiveGenerators.characters());
287    }
288
289    /**
290     * Factory method for creating a {@link TypedGenerator} for {@link Character}.
291     *
292     * @return a {@link TypedGenerator} for {@link Character}
293     */
294    public static TypedGenerator<Character> characterObjects() {
295        return new QuickCheckGeneratorAdapter<>(Character.class, PrimitiveGenerators.characters());
296    }
297
298    /**
299     * Factory method for creating a {@link TypedGenerator} for double primitives.
300     *
301     * @return a {@link TypedGenerator} for double primitives
302     */
303    public static TypedGenerator<Double> doubles() {
304        return new QuickCheckGeneratorAdapter<>(double.class, PrimitiveGenerators.doubles());
305    }
306
307    /**
308     * Factory method for creating a {@link TypedGenerator} for {@link Double}.
309     *
310     * @param low  lower bound of range
311     * @param high upper bound of range
312     * @return a {@link TypedGenerator} for {@link Double}
313     */
314    public static TypedGenerator<Double> doubles(final double low, final double high) {
315        return new QuickCheckGeneratorAdapter<>(Double.class, PrimitiveGenerators.doubles(low, high));
316    }
317
318    /**
319     * Factory method for creating a {@link TypedGenerator} for {@link Double}.
320     *
321     * @return a {@link TypedGenerator} for {@link Double}
322     */
323    public static TypedGenerator<Double> doubleObjects() {
324        return new QuickCheckGeneratorAdapter<>(Double.class, PrimitiveGenerators.doubles());
325    }
326
327    /**
328     * Factory method for creating a {@link TypedGenerator} for float primitives.
329     *
330     * @return a {@link TypedGenerator} for float primitives
331     */
332    public static TypedGenerator<Float> floats() {
333        return new DecoratorGenerator<>(float.class, floatObjects());
334    }
335
336    /**
337     * Factory method for creating a {@link TypedGenerator} for {@link Float}.
338     *
339     * @param low  lower bound of range
340     * @param high upper bound of range
341     * @return a {@link TypedGenerator} for {@link Float}
342     */
343    public static TypedGenerator<Float> floats(final float low, final float high) {
344        return new FloatObjectGenerator(low, high);
345    }
346
347    /**
348     * Factory method for creating a {@link TypedGenerator} for {@link Float}.
349     *
350     * @return a {@link TypedGenerator} for {@link Float}
351     */
352    public static TypedGenerator<Float> floatObjects() {
353        return new FloatObjectGenerator();
354    }
355
356    /**
357     * Factory method for creating a {@link TypedGenerator} for integer primitives.
358     *
359     * @return a {@link TypedGenerator} for integer primitives
360     */
361    public static TypedGenerator<Integer> integers() {
362        return new QuickCheckGeneratorAdapter<>(int.class, PrimitiveGenerators.integers());
363    }
364
365    /**
366     * Factory method for creating a {@link TypedGenerator} for {@link Integer}.
367     *
368     * @param low  lower bound of range
369     * @param high upper bound of range
370     * @return a {@link TypedGenerator} for {@link Integer}
371     */
372    public static TypedGenerator<Integer> integers(final int low, final int high) {
373        return new QuickCheckGeneratorAdapter<>(Integer.class, PrimitiveGenerators.integers(low, high));
374    }
375
376    /**
377     * Factory method for creating a {@link TypedGenerator} for {@link Integer}.
378     *
379     * @return a {@link TypedGenerator} for {@link Integer}
380     */
381    public static TypedGenerator<Integer> integerObjects() {
382        return new QuickCheckGeneratorAdapter<>(Integer.class, PrimitiveGenerators.integers());
383    }
384
385    /**
386     * Factory method for creating a {@link TypedGenerator} for {@link Number}.
387     *
388     * @return a {@link TypedGenerator} for {@link Number}
389     */
390    public static TypedGenerator<Number> numbers() {
391        return new NumberGenerator();
392    }
393
394    /**
395     * Factory method for creating a {@link TypedGenerator} for short primitives.
396     *
397     * @return a {@link TypedGenerator} for short primitives
398     */
399    public static TypedGenerator<Short> shorts() {
400        return new DecoratorGenerator<>(short.class, shortObjects());
401    }
402
403    /**
404     * Factory method for creating a {@link TypedGenerator} for {@link Short}.
405     *
406     * @return a {@link TypedGenerator} for {@link Short}
407     */
408    public static TypedGenerator<Short> shortObjects() {
409        return new ShortObjectGenerator();
410    }
411
412    /**
413     * Factory method for creating a {@link TypedGenerator} for long primitives.
414     *
415     * @return a {@link TypedGenerator} for long primitives
416     */
417    public static TypedGenerator<Long> longs() {
418        return new QuickCheckGeneratorAdapter<>(long.class, PrimitiveGenerators.longs());
419    }
420
421    /**
422     * Factory method for creating a {@link TypedGenerator} for Long primitives.
423     *
424     * @param low  lower bound of range
425     * @param high upper bound of range
426     * @return a {@link TypedGenerator} for long primitives
427     */
428    public static TypedGenerator<Long> longs(final long low, final long high) {
429        return new QuickCheckGeneratorAdapter<>(long.class, PrimitiveGenerators.longs(low, high));
430    }
431
432    /**
433     * Factory method for creating a {@link TypedGenerator} for {@link Long}.
434     *
435     * @return a {@link TypedGenerator} for {@link Long}
436     */
437    public static TypedGenerator<Long> longObjects() {
438        return new QuickCheckGeneratorAdapter<>(Long.class, PrimitiveGenerators.longs());
439    }
440
441    /**
442     * Factory method for creating a {@link TypedGenerator} for {@link Date}.
443     *
444     * @return a {@link TypedGenerator} for {@link Date}
445     */
446    public static TypedGenerator<Date> dates() {
447        return new QuickCheckGeneratorAdapter<>(Date.class, PrimitiveGenerators.dates());
448    }
449
450    /**
451     * Factory method for creating a {@link TypedGenerator} for {@link LocalDate}.
452     *
453     * @return a {@link TypedGenerator} for {@link LocalDate}
454     */
455    public static TypedGenerator<LocalDate> localDates() {
456        return new LocalDateGenerator();
457    }
458
459    /**
460     * Factory method for creating a {@link TypedGenerator} for {@link LocalTime}.
461     *
462     * @return a {@link TypedGenerator} for {@link LocalTime}
463     */
464    public static TypedGenerator<LocalTime> localTimes() {
465        return new LocalTimeGenerator();
466    }
467
468    /**
469     * Factory method for creating a {@link TypedGenerator} for
470     * {@link LocalDateTime}.
471     *
472     * @return a {@link TypedGenerator} for {@link LocalDateTime}
473     */
474    public static TypedGenerator<LocalDateTime> localDateTimes() {
475        return new LocalDateTimeGenerator();
476    }
477
478    /**
479     * Factory method for creating a {@link TypedGenerator} for
480     * {@link ZonedDateTime}.
481     *
482     * @return a {@link TypedGenerator} for {@link ZonedDateTime}
483     */
484    public static TypedGenerator<ZonedDateTime> zonedDateTimes() {
485        return new ZonedDateTimeGenerator();
486    }
487
488    /**
489     * Factory method for creating a {@link TypedGenerator} for {@link TimeZone}.
490     *
491     * @return a {@link TypedGenerator} for {@link TimeZone}
492     */
493    public static TypedGenerator<TimeZone> timeZones() {
494        final List<TimeZone> timezones = new ArrayList<>();
495        for (final String id : TimeZone.getAvailableIDs()) {
496            timezones.add(TimeZone.getTimeZone(id));
497        }
498        return fixedValues(TimeZone.class, timezones);
499    }
500
501    /**
502     * Factory method for creating a {@link TypedGenerator} for {@link ZoneId}.
503     *
504     * @return a {@link TypedGenerator} for {@link ZoneId}
505     */
506    public static TypedGenerator<ZoneId> zoneIds() {
507        return fixedValues(ZoneId.class,
508                ZoneId.getAvailableZoneIds().stream().map(ZoneId::of).collect(Collectors.toList()));
509    }
510
511    /**
512     * Factory method for creating a {@link TypedGenerator} for {@link ZoneOffset}.
513     *
514     * @return a {@link TypedGenerator} for {@link ZoneOffset}
515     */
516    public static TypedGenerator<ZoneOffset> zoneOffsets() {
517        return new ZoneOffsetGenerator();
518    }
519
520    /**
521     * Factory method for creating a {@link TypedGenerator} for {@link Temporal}s.
522     *
523     * @return a {@link TypedGenerator} for {@link Temporal}s
524     */
525    public static TypedGenerator<Temporal> temporals() {
526        return new TypedGenerator<>() {
527
528            @Override
529            public Class<Temporal> getType() {
530                return Temporal.class;
531            }
532
533            @Override
534            public Temporal next() {
535                return zonedDateTimes().next().toInstant();
536            }
537        };
538    }
539
540    // Advanced Java Types
541
542    /**
543     * Factory method for creating a {@link TypedGenerator} arbitrary {@link Class}
544     * Objects
545     *
546     * @return a {@link TypedGenerator} for the given values
547     */
548    @SuppressWarnings("rawtypes")
549    public static TypedGenerator<Class> classTypes() {
550        return fixedValues(Class.class, Integer.class, String.class, Boolean.class, Float.class);
551    }
552
553    /**
554     * Factory method for creating a {@link TypedGenerator} arbitrary {@link Locale}
555     * Objects
556     *
557     * @return a {@link TypedGenerator} for all {@link Locale}s
558     */
559    public static TypedGenerator<Locale> locales() {
560        return fixedValues(Locale.class, Locale.getAvailableLocales());
561    }
562
563    /**
564     * Factory method for creating a {@link TypedGenerator} arbitrary
565     * {@link Serializable} Objects
566     *
567     * @return a {@link TypedGenerator} for all {@link Serializable}s
568     */
569    @SuppressWarnings({ "unchecked", "rawtypes" })
570    public static TypedGenerator<Serializable> serializables() {
571        return new QuickCheckGeneratorAdapter(Serializable.class, PrimitiveGenerators.nonEmptyStrings());
572    }
573
574    /**
575     * Factory method for creating a {@link TypedGenerator} arbitrary
576     * {@link RuntimeException} Objects
577     *
578     * @return a {@link TypedGenerator} for all {@link RuntimeException}s
579     */
580    public static TypedGenerator<RuntimeException> runtimeExceptions() {
581        return fixedValues(RuntimeException.class, new RuntimeException(), new IllegalArgumentException(),
582                new IllegalStateException(), new NullPointerException());
583    }
584
585    /**
586     * Factory method for creating a {@link TypedGenerator} arbitrary
587     * {@link Throwable} Objects
588     *
589     * @return a {@link TypedGenerator} for all {@link Throwable}s
590     */
591    public static TypedGenerator<Throwable> throwables() {
592        return fixedValues(Throwable.class, new RuntimeException(), new IllegalArgumentException(),
593                new IllegalStateException(), new NullPointerException());
594    }
595
596    /**
597     * Factory method for creating a {@link TypedGenerator} arbitrary {@link URL}s
598     * Objects
599     *
600     * @return a {@link TypedGenerator} for all {@link URL}s
601     */
602    public static TypedGenerator<URL> urls() {
603        return new URLGenerator();
604    }
605
606    /**
607     * Factory method for creating a {@link TypedGenerator} from an existing
608     * QuickCheck {@link Generator}. Note: This method is for internal use only and
609     * will be removed soon!!!
610     *
611     * @param qcGenerator to be wrapped
612     * @param type        of the value
613     * @return a {@link TypedGenerator} for the given {@link Generator}
614     */
615    static <T> TypedGenerator<T> wrap(final Class<T> type, final Generator<T> qcGenerator) {
616        return new QuickCheckGeneratorAdapter<>(type, qcGenerator);
617    }
618
619    /**
620     * Factory method for creating a QuickCheck {@link Generator} from an existing
621     * {@link TypedGenerator}. Note: This method is for internal use only and will
622     * be removed soon!!!
623     *
624     * @param generator to be un-wrapped
625     * @return a {@link TypedGenerator} for the given {@link Generator}
626     */
627    static <T> Generator<T> unwrap(final TypedGenerator<T> generator) {
628        return generator::next;
629    }
630
631    /**
632     * Helper method that determines the actual type of a given {@link Iterable} by
633     * peeking into it. <em>For testing only, should never be used in productive
634     * code</em>
635     *
636     * @param iterable must not be null nor empty, the iterator must be reentrant.
637     * @return The Class of the given {@link Iterable}.
638     */
639    @SuppressWarnings("unchecked")
640    private static <T> Class<T> determineSupertypeFromIterable(final Iterable<T> iterable) {
641        requireNonNull(iterable, "iterable must not be null");
642        final var iterator = iterable.iterator();
643        if (iterator.hasNext()) {
644            return (Class<T>) iterator.next().getClass();
645        }
646        throw new IllegalArgumentException("Must contain at least a single element");
647    }
648}