Skip to content

Instantly share code, notes, and snippets.

@benravago
Created January 26, 2023 17:00
Show Gist options
  • Select an option

  • Save benravago/862c75eead3770ec1bf4e4d30983bc92 to your computer and use it in GitHub Desktop.

Select an option

Save benravago/862c75eead3770ec1bf4e4d30983bc92 to your computer and use it in GitHub Desktop.
A JUnit5 parameterized test argument provider and related annotation; provides named InputStream's as test input.
package lib.junit5.tools;
import static java.util.stream.Collectors.toList;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.support.AnnotationConsumer;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import static org.junit.jupiter.params.provider.Arguments.arguments;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.util.Preconditions;
class InputStreamArgumentsProvider implements AnnotationConsumer<InputStreamSource>, ArgumentsProvider {
private List<Input> sources;
record Input( Supplier<String> name, Function<ExtensionContext,InputStream> input ) {}
@Override // for AnnotationConsumer
public void accept(InputStreamSource annotation) {
var resources = Arrays.stream(annotation.resources()).map(this::resource);
var files = Arrays.stream(annotation.files()).map(Paths::get).map(this::file);
var paths = Arrays.stream(annotation.directories()).flatMap(this::directory).map(this::file);
sources = Stream.concat(resources, Stream.concat(files, paths)).collect(toList());
}
@Override // ArgumentsProvider
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Preconditions.notEmpty(this.sources, "sources must not be empty")
.stream().map( source -> arguments( source.name.get(), source.input.apply(context)) );
}
Input resource(String path) {
return new Input( () -> path, context -> open(path,context.getRequiredTestClass()) );
}
InputStream open(String path, Class<?> testClass) {
Preconditions.notBlank(path, () -> "resource name must not be null or blank");
var inputStream = testClass.getResourceAsStream(path);
return Preconditions.notNull(inputStream, () -> "resource not found at "+path);
}
Input file(Path path) {
return new Input( ()-> path.toString(), context -> open(path) );
}
InputStream open(Path path) {
Preconditions.notBlank(path.toString(), () -> "file path must not be null or blank");
try { return Files.newInputStream(path); }
catch (IOException e) { throw new JUnitException("file at" + path + " could not be read", e); }
}
static final Pattern globChars = Pattern.compile("[*?{}]");
Stream<Path> directory(String path) {
var dir = Paths.get(path);
var file = dir.getFileName().toString();
try (
var ds = globChars.matcher(file).find()
? Files.newDirectoryStream(dir.getParent(), file)
: Files.newDirectoryStream(dir);
) {
var list = new ArrayList<Path>();
ds.forEach(list::add);
return list.stream();
}
catch (IOException e) {
throw new JUnitException("directory at" + path + " could not be read", e);
}
}
}
package lib.junit5.tools;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.params.provider.ArgumentsSource;
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@ArgumentsSource(InputStreamArgumentsProvider.class)
public @interface InputStreamSource {
String[] resources() default {}; // resource names
String[] files() default {}; // file paths
String[] directories() default {}; // directory paths with optional glob in Path.fileName
}
@benravago
Copy link
Author

Usage example:

import java.io.InputStream;

import org.junit.jupiter.params.ParameterizedTest;
import lib.junit5.tools.InputStreamSource;

class Example {

  @ParameterizedTest(name="{index} -> {0}")
  @InputStreamSource(directories={"./test/files/*.xml"})
  void test(String name, InputStream input) {
    System.out.println(name+" => " + input);
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment