/* * Copyright 2015-2017 the original author or authors. * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution and is available at * * http://www.eclipse.org/legal/epl-v10.html */ package org.junit.jupiter.params.provider; import static java.util.Spliterators.spliteratorUnknownSize; import static java.util.stream.StreamSupport.stream; import static org.junit.jupiter.params.provider.ObjectArrayArguments.arguments; import java.io.InputStream; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Iterator; import java.util.Spliterator; import java.util.function.BiFunction; import java.util.stream.Stream; import com.univocity.parsers.csv.CsvParser; import com.univocity.parsers.csv.CsvParserSettings; import org.junit.jupiter.api.extension.ContainerExtensionContext; import org.junit.jupiter.params.support.AnnotationConsumer; import org.junit.platform.commons.JUnitException; import org.junit.platform.commons.util.Preconditions; /** * @since 5.0 */ class CsvFileArgumentsProvider implements ArgumentsProvider, AnnotationConsumer<CsvFileSource> { private final BiFunction<Class<?>, String, InputStream> inputStreamProvider; private String[] resources; private Charset charset; private CsvParserSettings settings; CsvFileArgumentsProvider() { this(Class::getResourceAsStream); } CsvFileArgumentsProvider(BiFunction<Class<?>, String, InputStream> inputStreamProvider) { this.inputStreamProvider = inputStreamProvider; } @Override public void accept(CsvFileSource annotation) { resources = annotation.resources(); charset = Charset.forName(annotation.encoding()); settings = new CsvParserSettings(); settings.getFormat().setDelimiter(annotation.delimiter()); settings.getFormat().setLineSeparator(annotation.lineSeparator()); settings.setAutoConfigurationEnabled(false); } @Override public Stream<? extends Arguments> provideArguments(ContainerExtensionContext context) { // @formatter:off return Arrays.stream(resources) .map(resource -> openInputStream(context, resource)) .map(this::createCsvParser) .flatMap(this::toStream); // @formatter:on } private InputStream openInputStream(ContainerExtensionContext context, String resource) { Class<?> testClass = context.getTestClass().orElseThrow( () -> new JUnitException("Cannot load classpath resource without test class")); return Preconditions.notNull(inputStreamProvider.apply(testClass, resource), () -> "Classpath resource does not exist: " + resource); } private CsvParser createCsvParser(InputStream inputStream) { CsvParser csvParser = new CsvParser(settings); csvParser.beginParsing(inputStream, charset); return csvParser; } private Stream<Arguments> toStream(CsvParser csvParser) { return stream(spliteratorUnknownSize(new CsvParserIterator(csvParser), Spliterator.ORDERED), false) // .onClose(csvParser::stopParsing); } private static class CsvParserIterator implements Iterator<Arguments> { private final CsvParser csvParser; private Object[] nextCsvRecord; CsvParserIterator(CsvParser csvParser) { this.csvParser = csvParser; advance(); } @Override public boolean hasNext() { return this.nextCsvRecord != null; } @Override public Arguments next() { Arguments result = arguments(this.nextCsvRecord); advance(); return result; } private void advance() { this.nextCsvRecord = csvParser.parseNext(); } } }