001/*
002 * Licensed to the author under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package de.cuioss.test.generator.internal.net.java.quickcheck;
018
019import java.io.PrintWriter;
020
021import lombok.experimental.UtilityClass;
022
023/**
024 * QuickCheck is an implementation of the Haskell QuickCheck generator based
025 * test tool (<a href="http://www.cs.chalmers.se/~rjmh/QuickCheck/">...</a>).
026 */
027@UtilityClass
028@SuppressWarnings("java:S106") // owolff ok for testcode
029public class QuickCheck {
030
031    public static final int MAX_NUMBER_OF_RUNS = 200;
032    public static final int MIN_NUMBER_OF_RUNS = 1;
033
034    public static final String SYSTEM_PROPERTY_RUNS = propertyName("RUNS");
035
036    /**
037     * Check the {@link Characteristic} for all values generated by the given
038     * {@link Generator}. The execution will fail fast if any of the calls of the
039     * {@link Characteristic#specify(Object)} method throws an exception.
040     *
041     * @param <T> type of the generated values
042     * @throws CharacteristicException if a characteristic is not {@code true} for a
043     *                                 generated value
044     * @throws GeneratorException      if generation of the next value failed.
045     */
046    public static <T> void forAll(Generator<T> generator, Characteristic<T> characteristic)
047            throws GeneratorException, CharacteristicException {
048        runner(characteristic, MAX_NUMBER_OF_RUNS, generator, new PrintWriter(new NullWriter())).forAll();
049    }
050
051    public static int getDefaultNumberOfRuns() {
052        var runs = Integer.getInteger(SYSTEM_PROPERTY_RUNS, MAX_NUMBER_OF_RUNS);
053        return Math.max(MIN_NUMBER_OF_RUNS, runs);
054    }
055
056    private static String propertyName(String name) {
057        return QuickCheck.class.getSimpleName() + "." + name;
058    }
059
060    /**
061     * Check the {@link Characteristic} for all values generated by the given
062     * {@link Generator}. The execution will fail fast if any of the calls of the
063     * {@link Characteristic#specify(Object)} method throws an exception.
064     *
065     * @param runs number of runs and generated values for this characteristic
066     * @param <T>  type of the generated values
067     * @throws CharacteristicException if a characteristic is not {@code true} for a
068     *                                 generated value
069     * @throws GeneratorException      if generation of the next value failed.
070     */
071    public static <T> void forAll(int runs, Generator<T> generator, Characteristic<T> characteristic)
072            throws GeneratorException, CharacteristicException {
073        runner(characteristic, runs, generator, new PrintWriter(new NullWriter())).forAll();
074    }
075
076    /**
077     * Check the {@link Characteristic} for all values generated by the given
078     * {@link Generator}. The execution will fail fast if any of the calls of the
079     * {@link Characteristic#specify(Object)} method throws an exception.
080     *
081     * @param <T> type of the generated values
082     * @throws CharacteristicException if a characteristic is not {@code true} for a
083     *                                 generated value
084     * @throws GeneratorException      if generation of the next value failed.
085     */
086    public static <T> void forAllVerbose(Generator<T> generator, Characteristic<T> characteristic)
087            throws GeneratorException, CharacteristicException {
088        runner(characteristic, MAX_NUMBER_OF_RUNS, generator, new PrintWriter(new PrintWriter(System.out))).forAll();
089    }
090
091    /**
092     * Check the {@link Characteristic} for all values generated by the given
093     * {@link Generator}. The execution will fail fast if any of the calls of the
094     * {@link Characteristic#specify(Object)} method throws an exception.
095     *
096     * @param runs number of runs and generated values for this characteristic
097     * @param <T>  type of the generated values
098     * @throws CharacteristicException if a characteristic is not {@code true} for a
099     *                                 generated value
100     * @throws GeneratorException      if generation of the next value failed.
101     */
102    public static <T> void forAllVerbose(int runs, Generator<T> generator, Characteristic<T> characteristic)
103            throws GeneratorException, CharacteristicException {
104        runner(characteristic, runs, generator, new PrintWriter(new PrintWriter(System.out))).forAll();
105    }
106
107    /**
108     * All executions of {@link Characteristic#specify(Object)} which execute this
109     * method will be skipped and a new test case will be generated. Execution will
110     * be stopped if it is not possible to create a new test cases after a
111     * reasonable amount of tries.
112     *
113     * @param predicate Skip the current test case if the predicate is true.
114     */
115    public static void guard(boolean predicate) {
116        if (!predicate) {
117            throw new GuardException();
118        }
119    }
120
121    private static <T> Runner<T> runner(Characteristic<T> characteristic, int runs, Generator<T> generator,
122            PrintWriter writer) {
123        return new RunnerImpl<>(characteristic, runs, generator, writer);
124    }
125}