![JAR search and dependency download from the Maven repository](/logo.png)
com.github.robtimus.junit.support.params.ArgumentsCombiner Maven / Gradle / Ivy
Show all versions of junit-support Show documentation
/*
* ArgumentsCombiner.java
* Copyright 2022 Rob Spoor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.robtimus.junit.support.params;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.EnumSource;
/**
* A class that can combine multiple arguments. Instances of this class can be used in {@link ArgumentsProvider} implementations or provider methods
* to produce argument combinations.
*
* This class has quite some overlapping functionality with
* JUnit Pioneer's {@literal @CartesianTest}. What this class adds that
* {@code @CartesianTest} doesn't is filtering out combinations.
*
* This class has no special support for enums like {@code @CartesianTest} and {@link EnumSource @EnumSource} do. That's because
* {@link #with(Collection)} and {@link #crossJoin(Collection)} can be used in combination with {@link EnumSet}. For instance, to exclude some enum
* constants, use {@link EnumSet#complementOf(EnumSet)} in combination with one of the other {@link EnumSet} factory methods.
*
* Note that instances of this class can be considered as builders for {@link Stream}s of {@link Arguments} instances. Like {@link Stream}s they
* should not be used more than once.
*
* @author Rob Spoor
* @since 2.0
*/
@SuppressWarnings("nls")
public final class ArgumentsCombiner {
private Stream extends Arguments> stream;
private ArgumentsCombiner(Stream extends Arguments> stream) {
this.stream = stream;
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* Note that this method does not support variable arguments; use {@link #with(Object...)} for that.
*
* @param arguments The initial set of arguments.
* @return The created arguments combiner.
*/
public static ArgumentsCombiner withBooleans(boolean[] arguments) {
Objects.requireNonNull(arguments);
Stream stream = IntStream.range(0, arguments.length)
.mapToObj(i -> arguments(arguments[i]));
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* Note that this method does not support variable arguments; use {@link #with(Object...)} for that.
*
* @param arguments The initial set of arguments.
* @return The created arguments combiner.
*/
public static ArgumentsCombiner withChars(char[] arguments) {
Objects.requireNonNull(arguments);
Stream stream = IntStream.range(0, arguments.length)
.mapToObj(i -> arguments(arguments[i]));
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* Note that this method does not support variable arguments; use {@link #with(Object...)} for that.
*
* @param arguments The initial set of arguments.
* @return The created arguments combiner.
*/
public static ArgumentsCombiner withBytes(byte[] arguments) {
Objects.requireNonNull(arguments);
Stream stream = IntStream.range(0, arguments.length)
.mapToObj(i -> arguments(arguments[i]));
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* Note that this method does not support variable arguments; use {@link #with(Object...)} for that.
*
* @param arguments The initial set of arguments.
* @return The created arguments combiner.
*/
public static ArgumentsCombiner withShorts(short[] arguments) {
Objects.requireNonNull(arguments);
Stream stream = IntStream.range(0, arguments.length)
.mapToObj(i -> arguments(arguments[i]));
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* Note that this method does not support variable arguments; use {@link #with(Object...)} for that.
*
* @param arguments The initial set of arguments.
* @return The created arguments combiner.
*/
public static ArgumentsCombiner withInts(int[] arguments) {
Objects.requireNonNull(arguments);
Stream stream = Arrays.stream(arguments)
.mapToObj(Arguments::arguments);
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* Note that this method does not support variable arguments; use {@link #with(Object...)} for that.
*
* @param arguments The initial set of arguments.
* @return The created arguments combiner.
*/
public static ArgumentsCombiner withLongs(long[] arguments) {
Objects.requireNonNull(arguments);
Stream stream = Arrays.stream(arguments)
.mapToObj(Arguments::arguments);
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* Note that this method does not support variable arguments; use {@link #with(Object...)} for that.
*
* @param arguments The initial set of arguments.
* @return The created arguments combiner.
*/
public static ArgumentsCombiner withFloats(float[] arguments) {
Objects.requireNonNull(arguments);
Stream stream = IntStream.range(0, arguments.length)
.mapToObj(i -> arguments(arguments[i]));
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* Note that this method does not support variable arguments; use {@link #with(Object...)} for that.
*
* @param arguments The initial set of arguments.
* @return The created arguments combiner.
*/
public static ArgumentsCombiner withDoubles(double[] arguments) {
Objects.requireNonNull(arguments);
Stream stream = Arrays.stream(arguments)
.mapToObj(Arguments::arguments);
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* @param arguments The initial set of arguments.
* @return The created arguments combiner.
*/
public static ArgumentsCombiner with(Object... arguments) {
Objects.requireNonNull(arguments);
Stream stream = Arrays.stream(arguments)
.map(Arguments::arguments);
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* @param arguments A collection with the initial set of arguments.
* @return The created arguments combiner.
* @throws NullPointerException If the given collection is {@code null}.
*/
public static ArgumentsCombiner with(Collection> arguments) {
Objects.requireNonNull(arguments);
Stream stream = arguments.stream()
.map(Arguments::arguments);
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* @param arguments A stream with the initial set of arguments. Note that this stream should not be used outside the returned instance anymore.
* @return The created arguments combiner.
* @throws NullPointerException If the given stream is {@code null}.
*/
public static ArgumentsCombiner with(Stream> arguments) {
Objects.requireNonNull(arguments);
Stream stream = arguments.map(ArgumentsCombiner::asArguments);
return new ArgumentsCombiner(stream);
}
/**
* Creates an arguments combiner with an initial set of arguments.
*
* Note that if the arguments provider throws an exception from its {@link ArgumentsProvider#provideArguments(ExtensionContext)} method,
* that exception will be re-thrown as is but as an unchecked exception.
*
* @param argumentsProvider The provider initial set of arguments.
* @param context The current extension context.
* @return The created arguments combiner.
* @throws NullPointerException If the given arguments provider or context is {@code null}.
*/
public static ArgumentsCombiner with(ArgumentsProvider argumentsProvider, ExtensionContext context) {
Objects.requireNonNull(argumentsProvider);
Objects.requireNonNull(context);
Stream extends Arguments> stream = argumentsStream(argumentsProvider, context);
return new ArgumentsCombiner(stream);
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* Note that this method does not support variable arguments; use {@link #crossJoin(Object...)} for that.
*
* @param arguments The arguments to add.
* @return This object.
*/
public ArgumentsCombiner crossJoinBooleans(boolean[] arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> IntStream.range(0, arguments.length)
.mapToObj(i -> combineArguments(args, arguments[i])));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* Note that this method does not support variable arguments; use {@link #crossJoin(Object...)} for that.
*
* @param arguments The arguments to add.
* @return This object.
*/
public ArgumentsCombiner crossJoinChars(char[] arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> IntStream.range(0, arguments.length)
.mapToObj(i -> combineArguments(args, arguments[i])));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* Note that this method does not support variable arguments; use {@link #crossJoin(Object...)} for that.
*
* @param arguments The arguments to add.
* @return This object.
*/
public ArgumentsCombiner crossJoinBytes(byte[] arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> IntStream.range(0, arguments.length)
.mapToObj(i -> combineArguments(args, arguments[i])));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* Note that this method does not support variable arguments; use {@link #crossJoin(Object...)} for that.
*
* @param arguments The arguments to add.
* @return This object.
*/
public ArgumentsCombiner crossJoinShorts(short[] arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> IntStream.range(0, arguments.length)
.mapToObj(i -> combineArguments(args, arguments[i])));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* Note that this method does not support variable arguments; use {@link #crossJoin(Object...)} for that.
*
* @param arguments The arguments to add.
* @return This object.
*/
public ArgumentsCombiner crossJoinInts(int[] arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> Arrays.stream(arguments)
.mapToObj(arg -> combineArguments(args, arg)));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* Note that this method does not support variable arguments; use {@link #crossJoin(Object...)} for that.
*
* @param arguments The arguments to add.
* @return This object.
*/
public ArgumentsCombiner crossJoinLongs(long[] arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> Arrays.stream(arguments)
.mapToObj(arg -> combineArguments(args, arg)));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* Note that this method does not support variable arguments; use {@link #crossJoin(Object...)} for that.
*
* @param arguments The arguments to add.
* @return This object.
*/
public ArgumentsCombiner crossJoinFloats(float[] arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> IntStream.range(0, arguments.length)
.mapToObj(i -> combineArguments(args, arguments[i])));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* Note that this method does not support variable arguments; use {@link #crossJoin(Object...)} for that.
*
* @param arguments The arguments to add.
* @return This object.
*/
public ArgumentsCombiner crossJoinDoubles(double[] arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> Arrays.stream(arguments)
.mapToObj(arg -> combineArguments(args, arg)));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* @param arguments The arguments to add.
* @return This object.
*/
public ArgumentsCombiner crossJoin(Object... arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> Arrays.stream(arguments)
.map(arg -> combineArguments(args, arg)));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* @param arguments A collection with the arguments to add.
* @return This object.
* @throws NullPointerException If the given collection is {@code null}.
*/
public ArgumentsCombiner crossJoin(Collection> arguments) {
Objects.requireNonNull(arguments);
stream = stream.flatMap(args -> arguments.stream()
.map(arg -> combineArguments(args, arg)));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* @param argumentsProvider A supplier for a stream with the arguments to add.
* Each invocation of the supplier should return a fresh new stream that is not used for any other purposes.
* @return This object.
* @throws NullPointerException If the given supplier is {@code null}.
*/
public ArgumentsCombiner crossJoin(Supplier extends Stream>> argumentsProvider) {
Objects.requireNonNull(argumentsProvider);
stream = stream.flatMap(args -> argumentsProvider.get()
.map(arg -> combineArguments(args, arg)));
return this;
}
/**
* Adds a set of arguments. The resulting arguments will be a cross join or Cartesian product of the current arguments and the given arguments.
*
* Note that if the arguments provider throws an exception from its {@link ArgumentsProvider#provideArguments(ExtensionContext)} method,
* that exception will be re-thrown as is but as an unchecked exception.
*
* @param argumentsProvider The provider for the arguments to add.
* @param context The current extension context.
* @return This object.
* @throws NullPointerException If the given arguments provider or context is {@code null}.
*/
public ArgumentsCombiner crossJoin(ArgumentsProvider argumentsProvider, ExtensionContext context) {
Objects.requireNonNull(argumentsProvider);
Objects.requireNonNull(context);
stream = stream.flatMap(args -> argumentsStream(argumentsProvider, context)
.map(args2 -> combineArguments(args, args2)));
return this;
}
/**
* Excludes a combination of values from the current combinations of arguments. If the combination occurs multiple times, all occurrences will be
* removed.
*
* Note that the number of values given should be equal to the number of arguments in each current combination. Otherwise, an exception will be
* thrown when the stream returned by {@link #stream()} is consumed.
*
* @param values The combination of values to exclude.
* @return This object.
*/
public ArgumentsCombiner excludeCombination(Object... values) {
Objects.requireNonNull(values);
Predicate