Add and use AbstractDecoderTestCase
Introduce new base test case for decoder tests, and use it. Issue: SPR-17449
This commit is contained in:
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.core.codec;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.AbstractLeakCheckingTestCase;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link Decoder} unit tests. Subclasses need to implement
|
||||
* {@link #canDecode()}, {@link #decode()} and {@link #decodeToMono()}, possibly using the wide
|
||||
* variety of helper methods like {@link #testDecodeAll} or {@link #testDecodeToMonoAll}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.1.3
|
||||
*/
|
||||
@SuppressWarnings("ProtectedField")
|
||||
public abstract class AbstractDecoderTestCase<D extends Decoder<?>>
|
||||
extends AbstractLeakCheckingTestCase {
|
||||
|
||||
/**
|
||||
* The decoder to test.
|
||||
*/
|
||||
protected D decoder;
|
||||
|
||||
/**
|
||||
* Construct a new {@code AbstractDecoderTestCase} for the given decoder.
|
||||
* @param decoder the decoder
|
||||
*/
|
||||
protected AbstractDecoderTestCase(D decoder) {
|
||||
Assert.notNull(decoder, "Encoder must not be null");
|
||||
|
||||
this.decoder = decoder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses should implement this method to test {@link Decoder#canDecode}.
|
||||
*/
|
||||
@Test
|
||||
public abstract void canDecode() throws Exception;
|
||||
|
||||
/**
|
||||
* Subclasses should implement this method to test {@link Decoder#decode}, possibly using
|
||||
* {@link #testDecodeAll} or other helper methods.
|
||||
*/
|
||||
@Test
|
||||
public abstract void decode() throws Exception;
|
||||
|
||||
/**
|
||||
* Subclasses should implement this method to test {@link Decoder#decodeToMono}, possibly using
|
||||
* {@link #testDecodeToMonoAll}.
|
||||
*/
|
||||
@Test
|
||||
public abstract void decodeToMono() throws Exception;
|
||||
|
||||
// Flux
|
||||
|
||||
/**
|
||||
* Helper methods that tests for a variety of {@link Flux} decoding scenarios. This methods
|
||||
* invokes:
|
||||
* <ul>
|
||||
* <li>{@link #testDecode(Publisher, ResolvableType, Consumer, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeError(Publisher, ResolvableType, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeCancel(Publisher, ResolvableType, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeEmpty(ResolvableType, MimeType, Map)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputClass the desired output class
|
||||
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output
|
||||
* @param <T> the output type
|
||||
*/
|
||||
protected <T> void testDecodeAll(Publisher<DataBuffer> input, Class<? extends T> outputClass,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer) {
|
||||
testDecodeAll(input, ResolvableType.forClass(outputClass), stepConsumer, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper methods that tests for a variety of {@link Flux} decoding scenarios. This methods
|
||||
* invokes:
|
||||
* <ul>
|
||||
* <li>{@link #testDecode(Publisher, ResolvableType, Consumer, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeError(Publisher, ResolvableType, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeCancel(Publisher, ResolvableType, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeEmpty(ResolvableType, MimeType, Map)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputType the desired output 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 <T> void testDecodeAll(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
testDecode(input, outputType, stepConsumer, mimeType, hints);
|
||||
testDecodeError(input, outputType, mimeType, hints);
|
||||
testDecodeCancel(input, outputType, mimeType, hints);
|
||||
testDecodeEmpty(outputType, mimeType, hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a standard {@link Decoder#decode decode} scenario. For example:
|
||||
* <pre class="code">
|
||||
* byte[] bytes1 = ...
|
||||
* byte[] bytes2 = ...
|
||||
*
|
||||
* Flux<DataBuffer> input = Flux.concat(
|
||||
* dataBuffer(bytes1),
|
||||
* dataBuffer(bytes2));
|
||||
*
|
||||
* testDecodeAll(input, byte[].class, step -> step
|
||||
* .consumeNextWith(expectBytes(bytes1))
|
||||
* .consumeNextWith(expectBytes(bytes2))
|
||||
* .verifyComplete());
|
||||
* </pre>
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputClass the desired output class
|
||||
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output
|
||||
* @param <T> the output type
|
||||
*/
|
||||
protected <T> void testDecode(Publisher<DataBuffer> input, Class<? extends T> outputClass,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer) {
|
||||
testDecode(input, ResolvableType.forClass(outputClass), stepConsumer, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a standard {@link Decoder#decode decode} scenario. For example:
|
||||
* <pre class="code">
|
||||
* byte[] bytes1 = ...
|
||||
* byte[] bytes2 = ...
|
||||
*
|
||||
* Flux<DataBuffer> input = Flux.concat(
|
||||
* dataBuffer(bytes1),
|
||||
* dataBuffer(bytes2));
|
||||
*
|
||||
* testDecodeAll(input, byte[].class, step -> step
|
||||
* .consumeNextWith(expectBytes(bytes1))
|
||||
* .consumeNextWith(expectBytes(bytes2))
|
||||
* .verifyComplete());
|
||||
* </pre>
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputType the desired output 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 testDecode(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Flux<T> result = (Flux<T>) this.decoder.decode(input, outputType, mimeType, hints);
|
||||
StepVerifier.FirstStep<T> step = StepVerifier.create(result);
|
||||
stepConsumer.accept(step);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a {@link Decoder#decode decode} scenario where the input stream contains an error.
|
||||
* This test method will feed the first element of the {@code input} stream to the decoder,
|
||||
* 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 decoder
|
||||
* @param outputType the desired output 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 void testDecodeError(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
input = Flux.concat(
|
||||
Flux.from(input).take(1),
|
||||
Flux.error(new InputException()));
|
||||
|
||||
Flux<?> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextCount(1)
|
||||
.expectError(InputException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a {@link Decoder#decode decode} scenario where the input stream is canceled.
|
||||
* This test method will feed the first element of the {@code input} stream to the decoder,
|
||||
* followed by a cancel signal.
|
||||
* The result is expected to contain one "normal" element.
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputType the desired output 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 void testDecodeCancel(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Flux<?> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNextCount(1)
|
||||
.thenCancel()
|
||||
.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a {@link Decoder#decode decode} scenario where the input stream is empty.
|
||||
* The output is expected to be empty as well.
|
||||
*
|
||||
* @param outputType the desired output 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 void testDecodeEmpty(ResolvableType outputType, @Nullable MimeType mimeType,
|
||||
@Nullable Map<String, Object> hints) {
|
||||
|
||||
Flux<DataBuffer> input = Flux.empty();
|
||||
Flux<?> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// Mono
|
||||
|
||||
/**
|
||||
* Helper methods that tests for a variety of {@link Mono} decoding scenarios. This methods
|
||||
* invokes:
|
||||
* <ul>
|
||||
* <li>{@link #testDecodeToMono(Publisher, ResolvableType, Consumer, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeToMonoError(Publisher, ResolvableType, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeToMonoCancel(Publisher, ResolvableType, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeToMonoEmpty(ResolvableType, MimeType, Map)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputClass the desired output class
|
||||
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output
|
||||
* @param <T> the output type
|
||||
*/
|
||||
protected <T> void testDecodeToMonoAll(Publisher<DataBuffer> input,
|
||||
Class<? extends T> outputClass, Consumer<StepVerifier.FirstStep<T>> stepConsumer) {
|
||||
|
||||
testDecodeToMonoAll(input, ResolvableType.forClass(outputClass), stepConsumer, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper methods that tests for a variety of {@link Mono} decoding scenarios. This methods
|
||||
* invokes:
|
||||
* <ul>
|
||||
* <li>{@link #testDecodeToMono(Publisher, ResolvableType, Consumer, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeToMonoError(Publisher, ResolvableType, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeToMonoCancel(Publisher, ResolvableType, MimeType, Map)}</li>
|
||||
* <li>{@link #testDecodeToMonoEmpty(ResolvableType, MimeType, Map)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputType the desired output 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 <T> void testDecodeToMonoAll(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
testDecodeToMono(input, outputType, stepConsumer, mimeType, hints);
|
||||
testDecodeToMonoError(input, outputType, mimeType, hints);
|
||||
testDecodeToMonoCancel(input, outputType, mimeType, hints);
|
||||
testDecodeToMonoEmpty(outputType, mimeType, hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a standard {@link Decoder#decodeToMono) decode} scenario. For example:
|
||||
* <pre class="code">
|
||||
* byte[] bytes1 = ...
|
||||
* byte[] bytes2 = ...
|
||||
* byte[] allBytes = ... // bytes1 + bytes2
|
||||
*
|
||||
* Flux<DataBuffer> input = Flux.concat(
|
||||
* dataBuffer(bytes1),
|
||||
* dataBuffer(bytes2));
|
||||
*
|
||||
* testDecodeAll(input, byte[].class, step -> step
|
||||
* .consumeNextWith(expectBytes(allBytes))
|
||||
* .verifyComplete());
|
||||
* </pre>
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputClass the desired output class
|
||||
* @param stepConsumer a consumer to {@linkplain StepVerifier verify} the output
|
||||
* @param <T> the output type
|
||||
*/
|
||||
protected <T> void testDecodeToMono(Publisher<DataBuffer> input,
|
||||
Class<? extends T> outputClass, Consumer<StepVerifier.FirstStep<T>> stepConsumer) {
|
||||
testDecodeToMono(input, ResolvableType.forClass(outputClass), stepConsumer, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a standard {@link Decoder#decodeToMono) decode} scenario. For example:
|
||||
* <pre class="code">
|
||||
* byte[] bytes1 = ...
|
||||
* byte[] bytes2 = ...
|
||||
* byte[] allBytes = ... // bytes1 + bytes2
|
||||
*
|
||||
* Flux<DataBuffer> input = Flux.concat(
|
||||
* dataBuffer(bytes1),
|
||||
* dataBuffer(bytes2));
|
||||
*
|
||||
* testDecodeAll(input, byte[].class, step -> step
|
||||
* .consumeNextWith(expectBytes(allBytes))
|
||||
* .verifyComplete());
|
||||
* </pre>
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputType the desired output 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 testDecodeToMono(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
Consumer<StepVerifier.FirstStep<T>> stepConsumer,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Mono<T> result = (Mono<T>) this.decoder.decodeToMono(input, outputType, mimeType, hints);
|
||||
StepVerifier.FirstStep<T> step = StepVerifier.create(result);
|
||||
stepConsumer.accept(step);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a {@link Decoder#decodeToMono decode} scenario where the input stream contains an error.
|
||||
* This test method will feed the first element of the {@code input} stream to the decoder,
|
||||
* followed by an {@link InputException}.
|
||||
* The result is expected to contain the error.
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputType the desired output 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 void testDecodeToMonoError(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
input = Flux.concat(
|
||||
Flux.from(input).take(1),
|
||||
Flux.error(new InputException()));
|
||||
|
||||
Mono<?> result = this.decoder.decodeToMono(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError(InputException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a {@link Decoder#decodeToMono decode} scenario where the input stream is canceled.
|
||||
* This test method will immediately cancel the output stream.
|
||||
*
|
||||
* @param input the input to be provided to the decoder
|
||||
* @param outputType the desired output 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 void testDecodeToMonoCancel(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
Mono<?> result = this.decoder.decodeToMono(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.thenCancel()
|
||||
.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a {@link Decoder#decodeToMono decode} scenario where the input stream is empty.
|
||||
* The output is expected to be empty as well.
|
||||
*
|
||||
* @param outputType the desired output 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 void testDecodeToMonoEmpty(ResolvableType outputType, @Nullable MimeType mimeType,
|
||||
@Nullable Map<String, Object> hints) {
|
||||
|
||||
Flux<DataBuffer> input = Flux.empty();
|
||||
Mono<?> result = this.decoder.decodeToMono(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a deferred {@link DataBuffer} containing the given bytes.
|
||||
* @param bytes the bytes that are to be stored in the buffer
|
||||
* @return the deferred buffer
|
||||
*/
|
||||
protected Mono<DataBuffer> dataBuffer(byte[] bytes) {
|
||||
return Mono.defer(() -> {
|
||||
DataBuffer dataBuffer = this.bufferFactory.allocateBuffer(bytes.length);
|
||||
dataBuffer.write(bytes);
|
||||
return Mono.just(dataBuffer);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception used in {@link #testDecodeError} and {@link #testDecodeToMonoError}
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static class InputException extends RuntimeException {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -20,17 +20,16 @@ import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.AbstractLeakCheckingTestCase;
|
||||
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.LeakAwareDataBufferFactory;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MimeType;
|
||||
@@ -46,13 +45,8 @@ import static org.junit.Assert.*;
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
@SuppressWarnings("ProtectedField")
|
||||
public abstract class AbstractEncoderTestCase<T, E extends Encoder<T>> {
|
||||
|
||||
/**
|
||||
* The data buffer factory used by the encoder.
|
||||
*/
|
||||
protected final DataBufferFactory bufferFactory =
|
||||
new LeakAwareDataBufferFactory();
|
||||
public abstract class AbstractEncoderTestCase<T, E extends Encoder<T>> extends
|
||||
AbstractLeakCheckingTestCase {
|
||||
|
||||
/**
|
||||
* The encoder to test.
|
||||
@@ -110,15 +104,6 @@ public abstract class AbstractEncoderTestCase<T, E extends Encoder<T>> {
|
||||
this.hints = hints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether any of the data buffers created by {@link #bufferFactory} have not been
|
||||
* released, throwing an assertion error if so.
|
||||
*/
|
||||
@After
|
||||
public final void checkForLeaks() {
|
||||
((LeakAwareDataBufferFactory) this.bufferFactory).checkForLeaks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract template method that provides input for the encoder.
|
||||
* Used for {@link #encode()}, {@link #encodeError()}, and {@link #encodeCancel()}.
|
||||
|
||||
@@ -16,16 +16,13 @@
|
||||
|
||||
package org.springframework.core.codec;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
@@ -34,11 +31,18 @@ import static org.junit.Assert.*;
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ByteArrayDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
public class ByteArrayDecoderTests extends AbstractDecoderTestCase<ByteArrayDecoder> {
|
||||
|
||||
private final ByteArrayDecoder decoder = new ByteArrayDecoder();
|
||||
private final byte[] fooBytes = "foo".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
private final byte[] barBytes = "bar".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
public ByteArrayDecoderTests() {
|
||||
super(new ByteArrayDecoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void canDecode() {
|
||||
assertTrue(this.decoder.canDecode(ResolvableType.forClass(byte[].class),
|
||||
@@ -49,50 +53,38 @@ public class ByteArrayDecoderTests extends AbstractDataBufferAllocatingTestCase
|
||||
MimeTypeUtils.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void decode() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
DataBuffer barBuffer = stringBuffer("bar");
|
||||
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
|
||||
Flux<byte[]> output = this.decoder.decode(source,
|
||||
ResolvableType.forClassWithGenerics(Publisher.class, byte[].class),
|
||||
null, Collections.emptyMap());
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
dataBuffer(this.fooBytes),
|
||||
dataBuffer(this.barBytes));
|
||||
|
||||
testDecodeAll(input, byte[].class, step -> step
|
||||
.consumeNextWith(expectBytes(this.fooBytes))
|
||||
.consumeNextWith(expectBytes(this.barBytes))
|
||||
.verifyComplete());
|
||||
|
||||
StepVerifier.create(output)
|
||||
.consumeNextWith(bytes -> assertArrayEquals("foo".getBytes(), bytes))
|
||||
.consumeNextWith(bytes -> assertArrayEquals("bar".getBytes(), bytes))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeError() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
Flux<DataBuffer> source =
|
||||
Flux.just(fooBuffer).concatWith(Flux.error(new RuntimeException()));
|
||||
Flux<byte[]> output = this.decoder.decode(source,
|
||||
ResolvableType.forClassWithGenerics(Publisher.class, byte[].class),
|
||||
null, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(output)
|
||||
.consumeNextWith(bytes -> assertArrayEquals("foo".getBytes(), bytes))
|
||||
.expectError()
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void decodeToMono() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
DataBuffer barBuffer = stringBuffer("bar");
|
||||
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
|
||||
Mono<byte[]> output = this.decoder.decodeToMono(source,
|
||||
ResolvableType.forClassWithGenerics(Publisher.class, byte[].class),
|
||||
null, Collections.emptyMap());
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
dataBuffer(this.fooBytes),
|
||||
dataBuffer(this.barBytes));
|
||||
|
||||
StepVerifier.create(output)
|
||||
.consumeNextWith(bytes -> assertArrayEquals("foobar".getBytes(), bytes))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
byte[] expected = new byte[this.fooBytes.length + this.barBytes.length];
|
||||
System.arraycopy(this.fooBytes, 0, expected, 0, this.fooBytes.length);
|
||||
System.arraycopy(this.barBytes, 0, expected, this.fooBytes.length, this.barBytes.length);
|
||||
|
||||
testDecodeToMonoAll(input, byte[].class, step -> step
|
||||
.consumeNextWith(expectBytes(expected))
|
||||
.verifyComplete());
|
||||
}
|
||||
|
||||
private Consumer<byte[]> expectBytes(byte[] expected) {
|
||||
return bytes -> assertArrayEquals(expected, bytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,16 +17,13 @@
|
||||
package org.springframework.core.codec;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
@@ -35,10 +32,18 @@ import static org.junit.Assert.*;
|
||||
/**
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class ByteBufferDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
public class ByteBufferDecoderTests extends AbstractDecoderTestCase<ByteBufferDecoder> {
|
||||
|
||||
private final ByteBufferDecoder decoder = new ByteBufferDecoder();
|
||||
private final byte[] fooBytes = "foo".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
private final byte[] barBytes = "bar".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
public ByteBufferDecoderTests() {
|
||||
super(new ByteBufferDecoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void canDecode() {
|
||||
assertTrue(this.decoder.canDecode(ResolvableType.forClass(ByteBuffer.class),
|
||||
@@ -49,48 +54,38 @@ public class ByteBufferDecoderTests extends AbstractDataBufferAllocatingTestCase
|
||||
MimeTypeUtils.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void decode() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
DataBuffer barBuffer = stringBuffer("bar");
|
||||
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
|
||||
Flux<ByteBuffer> output = this.decoder.decode(source,
|
||||
ResolvableType.forClassWithGenerics(Publisher.class, ByteBuffer.class),
|
||||
null, Collections.emptyMap());
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
dataBuffer(this.fooBytes),
|
||||
dataBuffer(this.barBytes));
|
||||
|
||||
testDecodeAll(input, ByteBuffer.class, step -> step
|
||||
.consumeNextWith(expectByteBuffer(ByteBuffer.wrap(this.fooBytes)))
|
||||
.consumeNextWith(expectByteBuffer(ByteBuffer.wrap(this.barBytes)))
|
||||
.verifyComplete());
|
||||
|
||||
|
||||
StepVerifier.create(output)
|
||||
.expectNext(ByteBuffer.wrap("foo".getBytes()), ByteBuffer.wrap("bar".getBytes()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeError() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
Flux<DataBuffer> source =
|
||||
Flux.just(fooBuffer).concatWith(Flux.error(new RuntimeException()));
|
||||
Flux<ByteBuffer> output = this.decoder.decode(source,
|
||||
ResolvableType.forClassWithGenerics(Publisher.class, ByteBuffer.class),
|
||||
null, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(output)
|
||||
.expectNext(ByteBuffer.wrap("foo".getBytes()))
|
||||
.expectError()
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void decodeToMono() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
DataBuffer barBuffer = stringBuffer("bar");
|
||||
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
|
||||
Mono<ByteBuffer> output = this.decoder.decodeToMono(source,
|
||||
ResolvableType.forClassWithGenerics(Publisher.class, ByteBuffer.class),
|
||||
null, Collections.emptyMap());
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
dataBuffer(this.fooBytes),
|
||||
dataBuffer(this.barBytes));
|
||||
ByteBuffer expected = ByteBuffer.allocate(this.fooBytes.length + this.barBytes.length);
|
||||
expected.put(this.fooBytes).put(this.barBytes).flip();
|
||||
|
||||
testDecodeToMonoAll(input, ByteBuffer.class, step -> step
|
||||
.consumeNextWith(expectByteBuffer(expected))
|
||||
.verifyComplete());
|
||||
|
||||
StepVerifier.create(output)
|
||||
.expectNext(ByteBuffer.wrap("foobar".getBytes()))
|
||||
.expectComplete()
|
||||
.verify();
|
||||
}
|
||||
|
||||
private Consumer<ByteBuffer> expectByteBuffer(ByteBuffer expected) {
|
||||
return actual -> assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,18 +17,14 @@
|
||||
package org.springframework.core.codec;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -36,10 +32,18 @@ import static org.junit.Assert.*;
|
||||
/**
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class DataBufferDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
public class DataBufferDecoderTests extends AbstractDecoderTestCase<DataBufferDecoder> {
|
||||
|
||||
private final DataBufferDecoder decoder = new DataBufferDecoder();
|
||||
private final byte[] fooBytes = "foo".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
private final byte[] barBytes = "bar".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
public DataBufferDecoderTests() {
|
||||
super(new DataBufferDecoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void canDecode() {
|
||||
assertTrue(this.decoder.canDecode(ResolvableType.forClass(DataBuffer.class),
|
||||
@@ -50,33 +54,40 @@ public class DataBufferDecoderTests extends AbstractDataBufferAllocatingTestCase
|
||||
MimeTypeUtils.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Override
|
||||
public void decode() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
DataBuffer barBuffer = stringBuffer("bar");
|
||||
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
|
||||
Flux<DataBuffer> output = this.decoder.decode(source,
|
||||
ResolvableType.forClassWithGenerics(Publisher.class, DataBuffer.class),
|
||||
null, Collections.emptyMap());
|
||||
Flux<DataBuffer> input = Flux.just(
|
||||
this.bufferFactory.wrap(this.fooBytes),
|
||||
this.bufferFactory.wrap(this.barBytes));
|
||||
|
||||
assertSame(source, output);
|
||||
|
||||
release(fooBuffer, barBuffer);
|
||||
testDecodeAll(input, DataBuffer.class, step -> step
|
||||
.consumeNextWith(expectDataBuffer(this.fooBytes))
|
||||
.consumeNextWith(expectDataBuffer(this.barBytes))
|
||||
.verifyComplete());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeToMono() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
DataBuffer barBuffer = stringBuffer("bar");
|
||||
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
|
||||
Mono<DataBuffer> output = this.decoder.decodeToMono(source,
|
||||
ResolvableType.forClassWithGenerics(Publisher.class, DataBuffer.class),
|
||||
null, Collections.emptyMap());
|
||||
@Override
|
||||
public void decodeToMono() throws Exception {
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
dataBuffer(this.fooBytes),
|
||||
dataBuffer(this.barBytes));
|
||||
|
||||
DataBuffer outputBuffer = output.block(Duration.ofSeconds(5));
|
||||
assertEquals("foobar", DataBufferTestUtils.dumpString(outputBuffer, StandardCharsets.UTF_8));
|
||||
byte[] expected = new byte[this.fooBytes.length + this.barBytes.length];
|
||||
System.arraycopy(this.fooBytes, 0, expected, 0, this.fooBytes.length);
|
||||
System.arraycopy(this.barBytes, 0, expected, this.fooBytes.length, this.barBytes.length);
|
||||
|
||||
release(outputBuffer);
|
||||
testDecodeToMonoAll(input, DataBuffer.class, step -> step
|
||||
.consumeNextWith(expectDataBuffer(expected))
|
||||
.verifyComplete());
|
||||
}
|
||||
|
||||
private Consumer<DataBuffer> expectDataBuffer(byte[] expected) {
|
||||
return actual -> {
|
||||
byte[] actualBytes = new byte[actual.readableByteCount()];
|
||||
actual.read(actualBytes);
|
||||
assertArrayEquals(expected, actualBytes);
|
||||
|
||||
DataBufferUtils.release(actual);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,17 +17,21 @@
|
||||
package org.springframework.core.codec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
@@ -37,10 +41,18 @@ import static org.springframework.core.ResolvableType.forClass;
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ResourceDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
public class ResourceDecoderTests extends AbstractDecoderTestCase<ResourceDecoder> {
|
||||
|
||||
private final ResourceDecoder decoder = new ResourceDecoder();
|
||||
private final byte[] fooBytes = "foo".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
private final byte[] barBytes = "bar".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
public ResourceDecoderTests() {
|
||||
super(new ResourceDecoder());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void canDecode() {
|
||||
assertTrue(this.decoder.canDecode(forClass(InputStreamResource.class), MimeTypeUtils.TEXT_PLAIN));
|
||||
@@ -50,16 +62,15 @@ public class ResourceDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
assertFalse(this.decoder.canDecode(forClass(Object.class), MimeTypeUtils.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void decode() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
DataBuffer barBuffer = stringBuffer("bar");
|
||||
Flux<DataBuffer> source = Flux.just(fooBuffer, barBuffer);
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
dataBuffer(this.fooBytes),
|
||||
dataBuffer(this.barBytes));
|
||||
|
||||
Flux<Resource> result = this.decoder
|
||||
.decode(source, forClass(Resource.class), null, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(result)
|
||||
testDecodeAll(input, Resource.class, step -> step
|
||||
.consumeNextWith(resource -> {
|
||||
try {
|
||||
byte[] bytes = StreamUtils.copyToByteArray(resource.getInputStream());
|
||||
@@ -70,22 +81,42 @@ public class ResourceDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
}
|
||||
})
|
||||
.expectComplete()
|
||||
.verify());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void testDecodeError(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
input = Flux.concat(
|
||||
Flux.from(input).take(1),
|
||||
Flux.error(new InputException()));
|
||||
|
||||
Flux<Resource> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError(InputException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeError() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo");
|
||||
Flux<DataBuffer> source =
|
||||
Flux.just(fooBuffer).concatWith(Flux.error(new RuntimeException()));
|
||||
@Override
|
||||
public void decodeToMono() throws Exception {
|
||||
Flux<DataBuffer> input = Flux.concat(
|
||||
dataBuffer(this.fooBytes),
|
||||
dataBuffer(this.barBytes));
|
||||
|
||||
|
||||
Flux<Resource> result = this.decoder
|
||||
.decode(source, forClass(Resource.class), null, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError()
|
||||
.verify();
|
||||
testDecodeToMonoAll(input, Resource.class, step -> step
|
||||
.consumeNextWith(resource -> {
|
||||
try {
|
||||
byte[] bytes = StreamUtils.copyToByteArray(resource.getInputStream());
|
||||
assertEquals("foobar", new String(bytes));
|
||||
}
|
||||
catch (IOException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
})
|
||||
.expectComplete()
|
||||
.verify());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,18 +17,20 @@
|
||||
package org.springframework.core.codec;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.MimeType;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
@@ -42,25 +44,28 @@ import static org.junit.Assert.*;
|
||||
* @author Brian Clozel
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class StringDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
public class StringDecoderTests extends AbstractDecoderTestCase<StringDecoder> {
|
||||
|
||||
private StringDecoder decoder = StringDecoder.allMimeTypes();
|
||||
private static final ResolvableType TYPE = ResolvableType.forClass(String.class);
|
||||
|
||||
public StringDecoderTests() {
|
||||
super(StringDecoder.allMimeTypes());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void canDecode() {
|
||||
assertTrue(this.decoder.canDecode(
|
||||
TYPE, MimeTypeUtils.TEXT_PLAIN));
|
||||
|
||||
assertTrue(this.decoder.canDecode(
|
||||
ResolvableType.forClass(String.class), MimeTypeUtils.TEXT_PLAIN));
|
||||
TYPE, MimeTypeUtils.TEXT_HTML));
|
||||
|
||||
assertTrue(this.decoder.canDecode(
|
||||
ResolvableType.forClass(String.class), MimeTypeUtils.TEXT_HTML));
|
||||
TYPE, MimeTypeUtils.APPLICATION_JSON));
|
||||
|
||||
assertTrue(this.decoder.canDecode(
|
||||
ResolvableType.forClass(String.class), MimeTypeUtils.APPLICATION_JSON));
|
||||
|
||||
assertTrue(this.decoder.canDecode(
|
||||
ResolvableType.forClass(String.class), MimeTypeUtils.parseMimeType("text/plain;charset=utf-8")));
|
||||
TYPE, MimeTypeUtils.parseMimeType("text/plain;charset=utf-8")));
|
||||
|
||||
|
||||
assertFalse(this.decoder.canDecode(
|
||||
@@ -70,19 +75,33 @@ public class StringDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
ResolvableType.forClass(Object.class), MimeTypeUtils.APPLICATION_JSON));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void decodeMultibyteCharacter() {
|
||||
public void decode() {
|
||||
String u = "ü";
|
||||
String e = "é";
|
||||
String o = "ø";
|
||||
String s = String.format("%s\n%s\n%s", u, e, o);
|
||||
Flux<DataBuffer> source = toDataBuffers(s, 1, UTF_8);
|
||||
Flux<DataBuffer> input = toDataBuffers(s, 1, UTF_8);
|
||||
|
||||
Flux<String> output = this.decoder.decode(source, ResolvableType.forClass(String.class),
|
||||
null, Collections.emptyMap());
|
||||
StepVerifier.create(output)
|
||||
testDecodeAll(input, ResolvableType.forClass(String.class), step -> step
|
||||
.expectNext(u, e, o)
|
||||
.verifyComplete();
|
||||
.verifyComplete(), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void testDecodeError(Publisher<DataBuffer> input, ResolvableType outputType,
|
||||
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
|
||||
|
||||
input = Flux.concat(
|
||||
Flux.from(input).take(1),
|
||||
Flux.error(new InputException()));
|
||||
|
||||
Flux<String> result = this.decoder.decode(input, outputType, mimeType, hints);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectError(InputException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -92,13 +111,11 @@ public class StringDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
String o = "ø";
|
||||
String s = String.format("%s\n%s\n%s", u, e, o);
|
||||
Flux<DataBuffer> source = toDataBuffers(s, 2, UTF_16BE);
|
||||
|
||||
MimeType mimeType = MimeTypeUtils.parseMimeType("text/plain;charset=utf-16be");
|
||||
Flux<String> output = this.decoder.decode(source, ResolvableType.forClass(String.class),
|
||||
mimeType, Collections.emptyMap());
|
||||
StepVerifier.create(output)
|
||||
|
||||
testDecode(source, TYPE, step -> step
|
||||
.expectNext(u, e, o)
|
||||
.verifyComplete();
|
||||
.verifyComplete(), mimeType, null);
|
||||
}
|
||||
|
||||
private Flux<DataBuffer> toDataBuffers(String s, int length, Charset charset) {
|
||||
@@ -115,7 +132,7 @@ public class StringDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
|
||||
@Test
|
||||
public void decodeNewLine() {
|
||||
Flux<DataBuffer> source = Flux.just(
|
||||
Flux<DataBuffer> input = Flux.just(
|
||||
stringBuffer("\r\nabc\n"),
|
||||
stringBuffer("def"),
|
||||
stringBuffer("ghi\r\n\n"),
|
||||
@@ -126,10 +143,7 @@ public class StringDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
stringBuffer("xyz")
|
||||
);
|
||||
|
||||
Flux<String> output = this.decoder.decode(source, ResolvableType.forClass(String.class),
|
||||
null, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(output)
|
||||
testDecode(input, String.class, step -> step
|
||||
.expectNext("")
|
||||
.expectNext("abc")
|
||||
.expectNext("defghi")
|
||||
@@ -138,15 +152,15 @@ public class StringDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
.expectNext("pqr")
|
||||
.expectNext("stuvwxyz")
|
||||
.expectComplete()
|
||||
.verify();
|
||||
.verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeNewLineIncludeDelimiters() {
|
||||
|
||||
decoder = StringDecoder.allMimeTypes(StringDecoder.DEFAULT_DELIMITERS, false);
|
||||
this.decoder = StringDecoder.allMimeTypes(StringDecoder.DEFAULT_DELIMITERS, false);
|
||||
|
||||
Flux<DataBuffer> source = Flux.just(
|
||||
Flux<DataBuffer> input = Flux.just(
|
||||
stringBuffer("\r\nabc\n"),
|
||||
stringBuffer("def"),
|
||||
stringBuffer("ghi\r\n\n"),
|
||||
@@ -157,10 +171,7 @@ public class StringDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
stringBuffer("xyz")
|
||||
);
|
||||
|
||||
Flux<String> output = this.decoder.decode(source, ResolvableType.forClass(String.class),
|
||||
null, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(output)
|
||||
testDecode(input, String.class, step -> step
|
||||
.expectNext("\r\n")
|
||||
.expectNext("abc\n")
|
||||
.expectNext("defghi\r\n")
|
||||
@@ -169,27 +180,23 @@ public class StringDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
.expectNext("pqr\n")
|
||||
.expectNext("stuvwxyz")
|
||||
.expectComplete()
|
||||
.verify();
|
||||
.verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeEmptyFlux() {
|
||||
Flux<DataBuffer> source = Flux.empty();
|
||||
Flux<String> output = this.decoder.decode(source, ResolvableType.forClass(String.class),
|
||||
null, Collections.emptyMap());
|
||||
Flux<DataBuffer> input = Flux.empty();
|
||||
|
||||
StepVerifier.create(output)
|
||||
.expectNextCount(0)
|
||||
testDecode(input, String.class, step -> step
|
||||
.expectComplete()
|
||||
.verify();
|
||||
|
||||
.verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeEmptyDataBuffer() {
|
||||
Flux<DataBuffer> source = Flux.just(stringBuffer(""));
|
||||
Flux<String> output = this.decoder.decode(source,
|
||||
ResolvableType.forClass(String.class), null, Collections.emptyMap());
|
||||
Flux<DataBuffer> input = Flux.just(stringBuffer(""));
|
||||
Flux<String> output = this.decoder.decode(input,
|
||||
TYPE, null, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(output)
|
||||
.expectNext("")
|
||||
@@ -197,45 +204,35 @@ public class StringDecoderTests extends AbstractDataBufferAllocatingTestCase {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeError() {
|
||||
DataBuffer fooBuffer = stringBuffer("foo\n");
|
||||
DataBuffer barBuffer = stringBuffer("bar");
|
||||
Flux<DataBuffer> source =
|
||||
Flux.just(fooBuffer, barBuffer).concatWith(Flux.error(new RuntimeException()));
|
||||
|
||||
Flux<String> output = this.decoder.decode(source,
|
||||
ResolvableType.forClass(String.class), null, Collections.emptyMap());
|
||||
|
||||
StepVerifier.create(output)
|
||||
.expectNext("foo")
|
||||
.expectError()
|
||||
.verify();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void decodeToMono() {
|
||||
Flux<DataBuffer> source = Flux.just(stringBuffer("foo"), stringBuffer("bar"), stringBuffer("baz"));
|
||||
Mono<String> output = this.decoder.decodeToMono(source,
|
||||
ResolvableType.forClass(String.class), null, Collections.emptyMap());
|
||||
Flux<DataBuffer> input = Flux.just(
|
||||
stringBuffer("foo"),
|
||||
stringBuffer("bar"),
|
||||
stringBuffer("baz"));
|
||||
|
||||
StepVerifier.create(output)
|
||||
testDecodeToMonoAll(input, String.class, step -> step
|
||||
.expectNext("foobarbaz")
|
||||
.expectComplete()
|
||||
.verify();
|
||||
.verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeToMonoWithEmptyFlux() throws InterruptedException {
|
||||
Flux<DataBuffer> source = Flux.empty();
|
||||
Mono<String> output = this.decoder.decodeToMono(source,
|
||||
ResolvableType.forClass(String.class), null, Collections.emptyMap());
|
||||
Flux<DataBuffer> input = Flux.empty();
|
||||
|
||||
StepVerifier.create(output)
|
||||
.expectNextCount(0)
|
||||
testDecodeToMono(input, String.class, step -> step
|
||||
.expectComplete()
|
||||
.verify();
|
||||
.verify());
|
||||
}
|
||||
|
||||
private DataBuffer stringBuffer(String value) {
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
DataBuffer buffer = this.bufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.core.io.buffer;
|
||||
|
||||
import org.junit.After;
|
||||
|
||||
/**
|
||||
* Abstract base class for unit tests that allocate data buffers via a {@link DataBufferFactory}.
|
||||
* After each unit test, this base class checks whether all created buffers have been released,
|
||||
* throwing an {@link AssertionError} if not.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.1.3
|
||||
* @see LeakAwareDataBufferFactory
|
||||
*/
|
||||
public abstract class AbstractLeakCheckingTestCase {
|
||||
|
||||
/**
|
||||
* The data buffer factory.
|
||||
*/
|
||||
@SuppressWarnings("ProtectedField")
|
||||
protected final LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory();
|
||||
|
||||
/**
|
||||
* Checks whether any of the data buffers created by {@link #bufferFactory} have not been
|
||||
* released, throwing an assertion error if so.
|
||||
*/
|
||||
@After
|
||||
public final void checkForLeaks() {
|
||||
this.bufferFactory.checkForLeaks();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,28 +26,13 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of the {@code DataBufferFactory} interface that keep track of memory leaks.
|
||||
* Useful for unit tests that handle data buffers. Simply call {@link #checkForLeaks()} in
|
||||
* a JUnit {@link After} method, and any buffers have not been released will result in an
|
||||
* Useful for unit tests that handle data buffers. Simply inherit from
|
||||
* {@link AbstractLeakCheckingTestCase} or call {@link #checkForLeaks()} in
|
||||
* a JUnit {@link After} method yourself, and any buffers have not been released will result in an
|
||||
* {@link AssertionError}.
|
||||
* <pre class="code">
|
||||
* public class MyUnitTest {
|
||||
*
|
||||
* private final LeakAwareDataBufferFactory bufferFactory =
|
||||
* new LeakAwareDataBufferFactory();
|
||||
*
|
||||
* @Test
|
||||
* public void doSomethingWithBufferFactory() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* @After
|
||||
* public void checkForLeaks() {
|
||||
* bufferFactory.checkForLeaks();
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
* @author Arjen Poutsma
|
||||
* @see LeakAwareDataBufferFactory
|
||||
*/
|
||||
public class LeakAwareDataBufferFactory implements DataBufferFactory {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user