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.junit.parameterized;
017
018import de.cuioss.test.generator.TypedGenerator;
019import org.junit.jupiter.params.provider.ArgumentsSource;
020
021import java.lang.annotation.Documented;
022import java.lang.annotation.ElementType;
023import java.lang.annotation.Retention;
024import java.lang.annotation.RetentionPolicy;
025import java.lang.annotation.Target;
026
027/**
028 * {@code @CompositeTypeGeneratorSource} is an {@link ArgumentsSource} that provides
029 * access to values from multiple {@link TypedGenerator} implementations for
030 * parameterized tests, generating combinations of values.
031 * 
032 * <p>
033 * This annotation allows you to combine multiple {@link TypedGenerator} implementations
034 * to generate combinations of values for your parameterized tests. The generators
035 * can be specified in three ways:
036 * </p>
037 * <ul>
038 *   <li>By class using {@code generatorClasses}</li>
039 *   <li>By method reference using {@code generatorMethods}</li>
040 *   <li>By generator type using {@code generators} with {@link GeneratorType} enum values</li>
041 * </ul>
042 * 
043 * <h2>Example Usage</h2>
044 * 
045 * <pre>
046 * {@code
047 * // Using generator classes
048 * @ParameterizedTest
049 * @CompositeTypeGeneratorSource(
050 *     generatorClasses = {
051 *         NonBlankStringGenerator.class,
052 *         IntegerGenerator.class
053 *     },
054 *     count = 3
055 * )
056 * void testWithMultipleGenerators(String text, Integer number) {
057 *     assertNotNull(text);
058 *     assertNotNull(number);
059 *     // Test with combinations of text and number
060 * }
061 * 
062 * // Using generator methods
063 * @ParameterizedTest
064 * @CompositeTypeGeneratorSource(
065 *     generatorMethods = {
066 *         "createStringGenerator",
067 *         "createIntegerGenerator"
068 *     },
069 *     count = 3
070 * )
071 * void testWithMultipleMethodGenerators(String text, Integer number) {
072 *     assertNotNull(text);
073 *     assertNotNull(number);
074 *     // Test with combinations of text and number
075 * }
076 * 
077 * // Using generator types from the Generators class
078 * @ParameterizedTest
079 * @CompositeTypeGeneratorSource(
080 *     generators = {
081 *         GeneratorType.NON_EMPTY_STRINGS,
082 *         GeneratorType.INTEGERS
083 *     },
084 *     count = 3
085 * )
086 * void testWithGeneratorTypes(String text, Integer number) {
087 *     assertNotNull(text);
088 *     assertNotNull(number);
089 *     // Test with combinations of text and number
090 * }
091 * 
092 * // Method generators
093 * static TypedGenerator<String> createStringGenerator() {
094 *     return Generators.strings(5, 10);
095 * }
096 * 
097 * static TypedGenerator<Integer> createIntegerGenerator() {
098 *     return Generators.integers(1, 100);
099 * }
100 * }
101 * </pre>
102 * 
103 * @author Oliver Wolff
104 * @since 2.0
105 * @see TypedGenerator
106 * @see CompositeTypeGeneratorArgumentsProvider
107 * @see GeneratorType
108 */
109@Target({ElementType.METHOD})
110@Retention(RetentionPolicy.RUNTIME)
111@Documented
112@ArgumentsSource(CompositeTypeGeneratorArgumentsProvider.class)
113public @interface CompositeTypeGeneratorSource {
114
115    /**
116     * The TypedGenerator classes to use for generating test values.
117     * Each class must have a no-args constructor.
118     * 
119     * @return the TypedGenerator classes
120     */
121    @SuppressWarnings("java:S1452") // This wildcard is because of the TypedGenerator interface. Ok for testing
122    Class<? extends TypedGenerator<?>>[] generatorClasses() default {};
123
124    /**
125     * The method names to invoke to get TypedGenerator instances.
126     * Methods can be in the test class or in external classes using the format
127     * "fully.qualified.ClassName#methodName".
128     * 
129     * @return the method names
130     */
131    String[] generatorMethods() default {};
132
133    /**
134     * The generator types to use from {@link de.cuioss.test.generator.Generators} class.
135     * This is a convenient way to use the standard generators without having to create
136     * custom generator classes or methods.
137     * 
138     * @return the generator types
139     */
140    GeneratorType[] generators() default {};
141
142    /**
143     * Number of combinations to generate.
144     * For each generator, this many values will be generated and combined
145     * in a cartesian product with values from other generators.
146     * 
147     * @return the number of values to generate per generator, defaults to 1
148     */
149    int count() default 1;
150
151    /**
152     * Whether to generate a cartesian product of all generator values.
153     * If true, all possible combinations of values from the generators will be created.
154     * If false, generators will be paired one-to-one (requires all generators to produce
155     * the same number of values).
156     * 
157     * @return true to generate a cartesian product, false for one-to-one pairing
158     */
159    boolean cartesianProduct() default true;
160
161}