001/* 002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de) 003 * 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 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 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.internal.net.java.quickcheck; 017 018import lombok.experimental.UtilityClass; 019 020import java.io.PrintWriter; 021 022/** 023 * QuickCheck is an implementation of the Haskell QuickCheck generator based 024 * test tool (<a href="http://www.cs.chalmers.se/~rjmh/QuickCheck/">...</a>). 025 */ 026@UtilityClass 027@SuppressWarnings("java:S106") // owolff ok for testcode 028public class QuickCheck { 029 030 public static final int MAX_NUMBER_OF_RUNS = 200; 031 public static final int MIN_NUMBER_OF_RUNS = 1; 032 033 public static final String SYSTEM_PROPERTY_RUNS = propertyName("RUNS"); 034 035 /** 036 * Check the {@link Characteristic} for all values generated by the given 037 * {@link Generator}. The execution will fail fast if any of the calls of the 038 * {@link Characteristic#specify(Object)} method throws an exception. 039 * 040 * @param <T> type of the generated values 041 * @throws CharacteristicException if a characteristic is not {@code true} for a 042 * generated value 043 * @throws GeneratorException if generation of the next value failed. 044 */ 045 public static <T> void forAll(Generator<T> generator, Characteristic<T> characteristic) 046 throws GeneratorException, CharacteristicException { 047 runner(characteristic, MAX_NUMBER_OF_RUNS, generator, new PrintWriter(new NullWriter())).forAll(); 048 } 049 050 public static int getDefaultNumberOfRuns() { 051 var runs = Integer.getInteger(SYSTEM_PROPERTY_RUNS, MAX_NUMBER_OF_RUNS); 052 return Math.max(MIN_NUMBER_OF_RUNS, runs); 053 } 054 055 private static String propertyName(String name) { 056 return QuickCheck.class.getSimpleName() + "." + name; 057 } 058 059 /** 060 * Check the {@link Characteristic} for all values generated by the given 061 * {@link Generator}. The execution will fail fast if any of the calls of the 062 * {@link Characteristic#specify(Object)} method throws an exception. 063 * 064 * @param runs number of runs and generated values for this characteristic 065 * @param <T> type of the generated values 066 * @throws CharacteristicException if a characteristic is not {@code true} for a 067 * generated value 068 * @throws GeneratorException if generation of the next value failed. 069 */ 070 public static <T> void forAll(int runs, Generator<T> generator, Characteristic<T> characteristic) 071 throws GeneratorException, CharacteristicException { 072 runner(characteristic, runs, generator, new PrintWriter(new NullWriter())).forAll(); 073 } 074 075 /** 076 * Check the {@link Characteristic} for all values generated by the given 077 * {@link Generator}. The execution will fail fast if any of the calls of the 078 * {@link Characteristic#specify(Object)} method throws an exception. 079 * 080 * @param <T> type of the generated values 081 * @throws CharacteristicException if a characteristic is not {@code true} for a 082 * generated value 083 * @throws GeneratorException if generation of the next value failed. 084 */ 085 public static <T> void forAllVerbose(Generator<T> generator, Characteristic<T> characteristic) 086 throws GeneratorException, CharacteristicException { 087 runner(characteristic, MAX_NUMBER_OF_RUNS, generator, new PrintWriter(new PrintWriter(System.out))).forAll(); 088 } 089 090 /** 091 * Check the {@link Characteristic} for all values generated by the given 092 * {@link Generator}. The execution will fail fast if any of the calls of the 093 * {@link Characteristic#specify(Object)} method throws an exception. 094 * 095 * @param runs number of runs and generated values for this characteristic 096 * @param <T> type of the generated values 097 * @throws CharacteristicException if a characteristic is not {@code true} for a 098 * generated value 099 * @throws GeneratorException if generation of the next value failed. 100 */ 101 public static <T> void forAllVerbose(int runs, Generator<T> generator, Characteristic<T> characteristic) 102 throws GeneratorException, CharacteristicException { 103 runner(characteristic, runs, generator, new PrintWriter(new PrintWriter(System.out))).forAll(); 104 } 105 106 /** 107 * All executions of {@link Characteristic#specify(Object)} which execute this 108 * method will be skipped and a new test case will be generated. Execution will 109 * be stopped if it is not possible to create a new test cases after a 110 * reasonable amount of tries. 111 * 112 * @param predicate Skip the current test case if the predicate is true. 113 */ 114 public static void guard(boolean predicate) { 115 if (!predicate) { 116 throw new GuardException(); 117 } 118 } 119 120 private static <T> Runner<T> runner(Characteristic<T> characteristic, int runs, Generator<T> generator, 121 PrintWriter writer) { 122 return new RunnerImpl<>(characteristic, runs, generator, writer); 123 } 124}