DataProviderUtil.java
package net.morimekta.testing.junit4;
import java.util.ArrayList;
import java.util.List;
/**
* Utility for using <code>junit-dataprovider</code>.
*/
public class DataProviderUtil {
/**
* This is a utility to be used with <code>junit-dataprovider</code>. It will instead of taking in an array of
* argument arrays, will get an array of available dimensions, each with possible values. It will then multiply all
* the possible dimension possibilities with each other and result the array of parameter array that
* <code>junit-dataprovider</code> wants.
* <p>
* E.g. providing two dimensions, content type and locale, e.g. like this:
*
* <pre>{@code
* {@literal@}RunWith(DataProviderRunner.class)
* public class MyTest {
* {@literal@}DataProvider
* public static Object[][] testParams() {
* return buildDataDimensions(
* List.of(ContentType.WALLPAPER, ContentType.AUDIO),
* List.of(Locale.US, Locale.DE, Locale.SIMPLIFIED_CHINESE));
* }
* {@literal@}Test
* {@literal@}UseDataProvider("testParams")
* public void testMyParams(ContentType contentType, Locale locale) {
* // The test.
* }
* }
* }</pre>
* <p>
* Will create 6 argument arrays, one for each combination of content type and locale as if the input was this:
*
* <pre>{@code
* return new Object[][]{
* {ContentType.WALLPAPER, Locale.US},
* {ContentType.WALLPAPER, Locale.DE},
* {ContentType.WALLPAPER, Locale.SIMPLIFIED_CHINESE},
* {ContentType.AUDIO, Locale.US},
* {ContentType.AUDIO, Locale.DE},
* {ContentType.AUDIO, Locale.SIMPLIFIED_CHINESE},
* });
* }</pre>
* <p>
* This can significantly shorten the argument list especially with larger set of dimensions and longer lists of
* options for each. It accepts null values and does not accept empty dimensions. Each dimension is called a 'layer'
* in the input.
*
* @param dimensions The available dimensions.
* @return The argument arrays.
*/
public static Object[][] buildDataDimensions(List<?>... dimensions) {
if (dimensions.length == 0) {
throw new IllegalArgumentException("No dimensions provided");
}
if (dimensions[0].size() == 0) {
throw new IllegalArgumentException("Empty dimension in layer 1");
}
List<List<Object>> result = new ArrayList<>();
for (Object o : dimensions[0]) {
List<Object> base = new ArrayList<>();
base.add(o);
result.add(base);
}
for (int layer = 1; layer < dimensions.length; ++layer) {
if (dimensions[layer].size() == 0) {
throw new IllegalArgumentException("Empty dimension in layer " + (layer + 1));
}
// The layer below is the one that will be multiplied with the
// arguments from this layer.
List<List<Object>> layerBelow = deepCopy(result);
Object first = dimensions[layer].get(0);
for (List<Object> l : result) {
l.add(first);
}
for (int pos = 1; pos < dimensions[layer].size(); ++pos) {
List<List<Object>> extraResults = deepCopy(layerBelow);
for (List<Object> l : extraResults) {
l.add(dimensions[layer].get(pos));
}
result.addAll(extraResults);
}
}
return makeDataParams(result);
}
/**
* Convenience method to be able to use lists instead of arrays to build the dimensions. This must be a a list of
* lists of equal sizes, with compatible argument objects in each list.
*
* @param lists List of list of arguments.
* @return Array of array of arguments.
*/
public static Object[][] makeDataParams(List<List<Object>> lists) {
// assumes all inner lists are the same size.
Object[][] out = new Object[lists.size()][];
for (int i = 0; i < lists.size(); ++i) {
out[i] = lists.get(i).toArray(EMPTY);
}
return out;
}
private static List<List<Object>> deepCopy(List<List<Object>> base) {
List<List<Object>> copy = new ArrayList<>(base.size());
for (List<Object> list : base) {
copy.add(new ArrayList<>(list));
}
return copy;
}
private static final Object[] EMPTY = new Object[0];
private DataProviderUtil() {
}
}