|
|
@ -18,7 +18,6 @@ package org.springframework.core.codec; |
|
|
|
|
|
|
|
|
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.function.Consumer; |
|
|
|
import java.util.function.Consumer; |
|
|
|
import java.util.stream.Stream; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.junit.Test; |
|
|
|
import org.junit.Test; |
|
|
|
import org.reactivestreams.Publisher; |
|
|
|
import org.reactivestreams.Publisher; |
|
|
@ -28,7 +27,6 @@ import reactor.test.StepVerifier; |
|
|
|
import org.springframework.core.ResolvableType; |
|
|
|
import org.springframework.core.ResolvableType; |
|
|
|
import org.springframework.core.io.buffer.AbstractLeakCheckingTestCase; |
|
|
|
import org.springframework.core.io.buffer.AbstractLeakCheckingTestCase; |
|
|
|
import org.springframework.core.io.buffer.DataBuffer; |
|
|
|
import org.springframework.core.io.buffer.DataBuffer; |
|
|
|
import org.springframework.core.io.buffer.DataBufferFactory; |
|
|
|
|
|
|
|
import org.springframework.core.io.buffer.DataBufferUtils; |
|
|
|
import org.springframework.core.io.buffer.DataBufferUtils; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
import org.springframework.lang.Nullable; |
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.Assert; |
|
|
@ -36,193 +34,245 @@ import org.springframework.util.MimeType; |
|
|
|
|
|
|
|
|
|
|
|
import static java.nio.charset.StandardCharsets.UTF_8; |
|
|
|
import static java.nio.charset.StandardCharsets.UTF_8; |
|
|
|
import static org.junit.Assert.*; |
|
|
|
import static org.junit.Assert.*; |
|
|
|
|
|
|
|
import static org.springframework.core.io.buffer.DataBufferUtils.release; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Abstract base class for {@link Encoder} unit tests. Subclasses need to implement |
|
|
|
* Abstract base class for {@link Encoder} unit tests. Subclasses need to implement |
|
|
|
* {@link #input()} and {@link #outputConsumers()}, from which {@link #encode()}, |
|
|
|
* {@link #canEncode()} and {@link #encode()}, possibly using the wide |
|
|
|
* {@link #encodeError()} and {@link #encodeCancel()} are run. |
|
|
|
* * variety of helper methods like {@link #testEncodeAll}. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Arjen Poutsma |
|
|
|
* @author Arjen Poutsma |
|
|
|
|
|
|
|
* @since 5.1.3 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@SuppressWarnings("ProtectedField") |
|
|
|
@SuppressWarnings("ProtectedField") |
|
|
|
public abstract class AbstractEncoderTestCase<T, E extends Encoder<T>> extends |
|
|
|
public abstract class AbstractEncoderTestCase<E extends Encoder<?>> |
|
|
|
AbstractLeakCheckingTestCase { |
|
|
|
extends AbstractLeakCheckingTestCase { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* The encoder to test. |
|
|
|
* The encoder to test. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected final E encoder; |
|
|
|
protected final E encoder; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* The type used for |
|
|
|
* Construct a new {@code AbstractEncoderTestCase} for the given parameters. |
|
|
|
* {@link Encoder#encode(Publisher, DataBufferFactory, ResolvableType, MimeType, Map)}. |
|
|
|
* @param encoder the encoder |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected final ResolvableType elementType; |
|
|
|
protected AbstractEncoderTestCase(E encoder) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Assert.notNull(encoder, "Encoder must not be null"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.encoder = encoder; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* The mime type used for |
|
|
|
* Subclasses should implement this method to test {@link Encoder#canEncode}. |
|
|
|
* {@link Encoder#encode(Publisher, DataBufferFactory, ResolvableType, MimeType, Map)}. |
|
|
|
|
|
|
|
* May be {@code null}. |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Nullable |
|
|
|
@Test |
|
|
|
protected final MimeType mimeType; |
|
|
|
public abstract void canEncode() throws Exception; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* The hints used for |
|
|
|
* Subclasses should implement this method to test {@link Encoder#encode}, possibly using |
|
|
|
* {@link Encoder#encode(Publisher, DataBufferFactory, ResolvableType, MimeType, Map)}. |
|
|
|
* {@link #testEncodeAll} or other helper methods. |
|
|
|
* May be {@code null}. |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Nullable |
|
|
|
@Test |
|
|
|
protected final Map<String, Object> hints; |
|
|
|
public abstract void encode() throws Exception; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Construct a new {@code AbstractEncoderTestCase} for the given encoder and element class. |
|
|
|
* Helper methods that tests for a variety of encoding scenarios. This methods |
|
|
|
* @param encoder the encoder |
|
|
|
* invokes: |
|
|
|
* @param elementClass the element class |
|
|
|
* <ul> |
|
|
|
|
|
|
|
* <li>{@link #testEncode(Publisher, ResolvableType, Consumer, MimeType, Map)}</li> |
|
|
|
|
|
|
|
* <li>{@link #testEncodeError(Publisher, ResolvableType, MimeType, Map)}</li> |
|
|
|
|
|
|
|
* <li>{@link #testEncodeCancel(Publisher, ResolvableType, MimeType, Map)}</li> |
|
|
|
|
|
|
|
* <li>{@link #testEncodeEmpty(ResolvableType, MimeType, Map)}</li> |
|
|
|
|
|
|
|
* </ul> |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param input the input to be provided to the encoder |
|
|
|
|
|
|
|
* @param inputClass the input class |
|
|
|
|
|
|
|
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output |
|
|
|
|
|
|
|
* @param <T> the output type |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected AbstractEncoderTestCase(E encoder, Class<?> elementClass) { |
|
|
|
protected <T> void testEncodeAll(Publisher<? extends T> input, Class<? extends T> inputClass, |
|
|
|
this(encoder, ResolvableType.forClass(elementClass), null, null); |
|
|
|
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer) { |
|
|
|
|
|
|
|
testEncodeAll(input, ResolvableType.forClass(inputClass), stepConsumer, null, null); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Construct a new {@code AbstractEncoderTestCase} for the given parameters. |
|
|
|
* Helper methods that tests for a variety of decoding scenarios. This methods |
|
|
|
* @param encoder the encoder |
|
|
|
* invokes: |
|
|
|
* @param elementType the element type |
|
|
|
* <ul> |
|
|
|
* @param mimeType the mime type. May be {@code null}. |
|
|
|
* <li>{@link #testEncode(Publisher, ResolvableType, Consumer, MimeType, Map)}</li> |
|
|
|
* @param hints the hints. May be {@code null}. |
|
|
|
* <li>{@link #testEncodeError(Publisher, ResolvableType, MimeType, Map)}</li> |
|
|
|
|
|
|
|
* <li>{@link #testEncodeCancel(Publisher, ResolvableType, MimeType, Map)}</li> |
|
|
|
|
|
|
|
* <li>{@link #testEncodeEmpty(ResolvableType, MimeType, Map)}</li> |
|
|
|
|
|
|
|
* </ul> |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param input the input to be provided to the encoder |
|
|
|
|
|
|
|
* @param inputType the input type |
|
|
|
|
|
|
|
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output |
|
|
|
|
|
|
|
* @param mimeType the mime type to use for decoding. May be {@code null}. |
|
|
|
|
|
|
|
* @param hints the hints used for decoding. May be {@code null}. |
|
|
|
|
|
|
|
* @param <T> the output type |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected AbstractEncoderTestCase(E encoder, ResolvableType elementType, |
|
|
|
protected <T> void testEncodeAll(Publisher<? extends T> input, ResolvableType inputType, |
|
|
|
|
|
|
|
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer, |
|
|
|
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { |
|
|
|
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { |
|
|
|
|
|
|
|
testEncode(input, inputType, stepConsumer, mimeType, hints); |
|
|
|
|
|
|
|
testEncodeError(input, inputType, mimeType, hints); |
|
|
|
|
|
|
|
testEncodeCancel(input, inputType, mimeType, hints); |
|
|
|
|
|
|
|
testEncodeEmpty(inputType, mimeType, hints); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Assert.notNull(encoder, "Encoder must not be null"); |
|
|
|
/** |
|
|
|
Assert.notNull(elementType, "ElementType must not be null"); |
|
|
|
* Test a standard {@link Encoder#encode encode} scenario. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param input the input to be provided to the encoder |
|
|
|
|
|
|
|
* @param inputClass the input class |
|
|
|
|
|
|
|
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output |
|
|
|
|
|
|
|
* @param <T> the output type |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected <T> void testEncode(Publisher<? extends T> input, Class<? extends T> inputClass, |
|
|
|
|
|
|
|
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer) { |
|
|
|
|
|
|
|
testEncode(input, ResolvableType.forClass(inputClass), stepConsumer, null, null); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.encoder = encoder; |
|
|
|
/** |
|
|
|
this.elementType = elementType; |
|
|
|
* Test a standard {@link Encoder#encode encode} scenario. |
|
|
|
this.mimeType = mimeType; |
|
|
|
* |
|
|
|
this.hints = hints; |
|
|
|
* @param input the input to be provided to the encoder |
|
|
|
|
|
|
|
* @param inputType the input type |
|
|
|
|
|
|
|
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output |
|
|
|
|
|
|
|
* @param mimeType the mime type to use for decoding. May be {@code null}. |
|
|
|
|
|
|
|
* @param hints the hints used for decoding. May be {@code null}. |
|
|
|
|
|
|
|
* @param <T> the output type |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
|
|
|
protected <T> void testEncode(Publisher<? extends T> input, ResolvableType inputType, |
|
|
|
|
|
|
|
Consumer<StepVerifier.FirstStep<DataBuffer>> stepConsumer, |
|
|
|
|
|
|
|
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Flux<DataBuffer> result = encoder().encode(input, this.bufferFactory, inputType, |
|
|
|
|
|
|
|
mimeType, hints); |
|
|
|
|
|
|
|
StepVerifier.FirstStep<DataBuffer> step = StepVerifier.create(result); |
|
|
|
|
|
|
|
stepConsumer.accept(step); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Abstract template method that provides input for the encoder. |
|
|
|
* Test a {@link Encoder#encode encode} scenario where the input stream contains an error. |
|
|
|
* Used for {@link #encode()}, {@link #encodeError()}, and {@link #encodeCancel()}. |
|
|
|
* This test method will feed the first element of the {@code input} stream to the encoder, |
|
|
|
|
|
|
|
* followed by an {@link InputException}. |
|
|
|
|
|
|
|
* The result is expected to contain one "normal" element, followed by the error. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param input the input to be provided to the encoder |
|
|
|
|
|
|
|
* @param inputType the input type |
|
|
|
|
|
|
|
* @param mimeType the mime type to use for decoding. May be {@code null}. |
|
|
|
|
|
|
|
* @param hints the hints used for decoding. May be {@code null}. |
|
|
|
|
|
|
|
* @see InputException |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected abstract Flux<T> input(); |
|
|
|
protected void testEncodeError(Publisher<?> input, ResolvableType inputType, |
|
|
|
|
|
|
|
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
input = Flux.concat( |
|
|
|
|
|
|
|
Flux.from(input).take(1), |
|
|
|
|
|
|
|
Flux.error(new InputException())); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Flux<DataBuffer> result = encoder().encode(input, this.bufferFactory, inputType, |
|
|
|
|
|
|
|
mimeType, hints); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StepVerifier.create(result) |
|
|
|
|
|
|
|
.consumeNextWith(DataBufferUtils::release) |
|
|
|
|
|
|
|
.expectError(InputException.class) |
|
|
|
|
|
|
|
.verify(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Abstract template method that verifies the output of the encoder. |
|
|
|
* Test a {@link Encoder#encode encode} scenario where the input stream is canceled. |
|
|
|
* The returned stream should contain a buffer consumer for each expected output, given |
|
|
|
* This test method will feed the first element of the {@code input} stream to the decoder, |
|
|
|
* the {@linkplain #input()}. |
|
|
|
* followed by a cancel signal. |
|
|
|
|
|
|
|
* The result is expected to contain one "normal" element. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param input the input to be provided to the encoder |
|
|
|
|
|
|
|
* @param inputType the input type |
|
|
|
|
|
|
|
* @param mimeType the mime type to use for decoding. May be {@code null}. |
|
|
|
|
|
|
|
* @param hints the hints used for decoding. May be {@code null}. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected abstract Stream<Consumer<DataBuffer>> outputConsumers(); |
|
|
|
protected void testEncodeCancel(Publisher<?> input, ResolvableType inputType, |
|
|
|
|
|
|
|
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { |
|
|
|
|
|
|
|
|
|
|
|
private Stream<Consumer<DataBuffer>> outputAndReleaseConsumers() { |
|
|
|
Flux<DataBuffer> result = encoder().encode(input, this.bufferFactory, inputType, mimeType, |
|
|
|
return outputConsumers() |
|
|
|
hints); |
|
|
|
.map(consumer -> consumer.andThen(DataBufferUtils::release)); |
|
|
|
|
|
|
|
|
|
|
|
StepVerifier.create(result) |
|
|
|
|
|
|
|
.consumeNextWith(DataBufferUtils::release) |
|
|
|
|
|
|
|
.thenCancel() |
|
|
|
|
|
|
|
.verify(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Create a result consumer that expects the given String in UTF-8 encoding. |
|
|
|
* Test a {@link Encoder#encode encode} scenario where the input stream is empty. |
|
|
|
* @param expected the expected string |
|
|
|
* The output is expected to be empty as well. |
|
|
|
* @return a consumer that expects the given data buffer to be equal to {@code expected} |
|
|
|
* |
|
|
|
|
|
|
|
* @param inputType the input type |
|
|
|
|
|
|
|
* @param mimeType the mime type to use for decoding. May be {@code null}. |
|
|
|
|
|
|
|
* @param hints the hints used for decoding. May be {@code null}. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected final Consumer<DataBuffer> resultConsumer(String expected) { |
|
|
|
protected void testEncodeEmpty(ResolvableType inputType, @Nullable MimeType mimeType, |
|
|
|
return dataBuffer -> { |
|
|
|
@Nullable Map<String, Object> hints) { |
|
|
|
byte[] resultBytes = new byte[dataBuffer.readableByteCount()]; |
|
|
|
|
|
|
|
dataBuffer.read(resultBytes); |
|
|
|
|
|
|
|
String actual = new String(resultBytes, UTF_8); |
|
|
|
|
|
|
|
assertEquals(expected, actual); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Flux<?> input = Flux.empty(); |
|
|
|
|
|
|
|
Flux<DataBuffer> result = encoder().encode(input, this.bufferFactory, inputType, |
|
|
|
|
|
|
|
mimeType, hints); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StepVerifier.create(result) |
|
|
|
|
|
|
|
.verifyComplete(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Create a result consumer that expects the given bytes. |
|
|
|
* Create a result consumer that expects the given bytes. |
|
|
|
* @param expected the expected string |
|
|
|
* @param expected the expected bytes |
|
|
|
* @return a consumer that expects the given data buffer to be equal to {@code expected} |
|
|
|
* @return a consumer that expects the given data buffer to be equal to {@code expected} |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected final Consumer<DataBuffer> resultConsumer(byte[] expected) { |
|
|
|
protected final Consumer<DataBuffer> expectBytes(byte[] expected) { |
|
|
|
return dataBuffer -> { |
|
|
|
return dataBuffer -> { |
|
|
|
byte[] resultBytes = new byte[dataBuffer.readableByteCount()]; |
|
|
|
byte[] resultBytes = new byte[dataBuffer.readableByteCount()]; |
|
|
|
dataBuffer.read(resultBytes); |
|
|
|
dataBuffer.read(resultBytes); |
|
|
|
|
|
|
|
release(dataBuffer); |
|
|
|
assertArrayEquals(expected, resultBytes); |
|
|
|
assertArrayEquals(expected, resultBytes); |
|
|
|
}; |
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Tests whether passing {@link #input()} to the encoder can be consumed with |
|
|
|
* Create a result consumer that expects the given string, using the UTF-8 encoding. |
|
|
|
* {@link #outputConsumers()}. |
|
|
|
* @param expected the expected string |
|
|
|
|
|
|
|
* @return a consumer that expects the given data buffer to be equal to {@code expected} |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Test |
|
|
|
protected Consumer<DataBuffer> expectString(String expected) { |
|
|
|
public final void encode() { |
|
|
|
return dataBuffer -> { |
|
|
|
Flux<T> input = input(); |
|
|
|
byte[] resultBytes = new byte[dataBuffer.readableByteCount()]; |
|
|
|
|
|
|
|
dataBuffer.read(resultBytes); |
|
|
|
Flux<DataBuffer> output = this.encoder.encode(input, this.bufferFactory, |
|
|
|
release(dataBuffer); |
|
|
|
this.elementType, this.mimeType, this.hints); |
|
|
|
String actual = new String(resultBytes, UTF_8); |
|
|
|
|
|
|
|
assertEquals(expected, actual); |
|
|
|
StepVerifier.Step<DataBuffer> step = StepVerifier.create(output); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
outputAndReleaseConsumers().forEach(step::consumeNextWith); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
step.expectComplete() |
|
|
|
|
|
|
|
.verify(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
* Tests whether passing an error to the encoder can be consumed with |
|
|
|
private <T> Encoder<T> encoder() { |
|
|
|
* {@link #outputConsumers()}. |
|
|
|
return (Encoder<T>) this.encoder; |
|
|
|
*/ |
|
|
|
|
|
|
|
@Test |
|
|
|
|
|
|
|
public final void encodeError() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
boolean singleValue = this.encoder instanceof AbstractSingleValueEncoder; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Flux<T> input; |
|
|
|
|
|
|
|
if (singleValue) { |
|
|
|
|
|
|
|
input = Flux.error(new RuntimeException()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
input = Flux.concat( |
|
|
|
|
|
|
|
input().take(1), |
|
|
|
|
|
|
|
Flux.error(new RuntimeException())); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Flux<DataBuffer> output = this.encoder.encode(input, this.bufferFactory, |
|
|
|
|
|
|
|
this.elementType, this.mimeType, this.hints); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (singleValue) { |
|
|
|
|
|
|
|
StepVerifier.create(output) |
|
|
|
|
|
|
|
.expectError(RuntimeException.class) |
|
|
|
|
|
|
|
.verify(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
Consumer<DataBuffer> firstResultConsumer = outputAndReleaseConsumers().findFirst() |
|
|
|
|
|
|
|
.orElseThrow(IllegalArgumentException::new); |
|
|
|
|
|
|
|
StepVerifier.create(output) |
|
|
|
|
|
|
|
.consumeNextWith(firstResultConsumer) |
|
|
|
|
|
|
|
.expectError(RuntimeException.class) |
|
|
|
|
|
|
|
.verify(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Tests whether canceling the output of the encoder can be consumed with |
|
|
|
* Exception used in {@link #testEncodeError}. |
|
|
|
* {@link #outputConsumers()}. |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@Test |
|
|
|
@SuppressWarnings("serial") |
|
|
|
public final void encodeCancel() { |
|
|
|
public static class InputException extends RuntimeException { |
|
|
|
Flux<T> input = input(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Flux<DataBuffer> output = this.encoder.encode(input, this.bufferFactory, |
|
|
|
|
|
|
|
this.elementType, this.mimeType, this.hints); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Consumer<DataBuffer> firstResultConsumer = outputAndReleaseConsumers().findFirst() |
|
|
|
|
|
|
|
.orElseThrow(IllegalArgumentException::new); |
|
|
|
|
|
|
|
StepVerifier.create(output) |
|
|
|
|
|
|
|
.consumeNextWith(firstResultConsumer) |
|
|
|
|
|
|
|
.thenCancel() |
|
|
|
|
|
|
|
.verify(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|