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}