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}