blob: 4329f1efde758b41b1b2284b94e272a779c5a7f3 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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 com.android.apksig.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.junit.Test;
/**
* Base class for testing implementations of {@link DataSink}. This class tests the contract of
* {@code DataSink}.
*
* <p>To subclass, provide an implementation of {@link #createDataSink()} which returns the
* implementation of {@code DataSink} you want to test.
*/
public abstract class DataSinkTestBase<T extends DataSink> {
/**
* Returns a new {@link DataSink}.
*/
protected abstract CloseableWithDataSink<T> createDataSink() throws IOException;
/**
* Returns the contents of the data sink.
*/
protected abstract ByteBuffer getContents(T dataSink) throws IOException;
@Test
public void testConsumeFromArray() throws Exception {
try (CloseableWithDataSink<T> c = createDataSink()) {
T sink = c.getDataSink();
byte[] input = "abcdefg".getBytes(StandardCharsets.UTF_8);
sink.consume(input, 2, 3); // "cde"
sink.consume(input, 0, 1); // "a"
assertContentsEquals("cdea", sink);
// Zero-length chunks
sink.consume(input, 0, 0);
sink.consume(input, 1, 0);
sink.consume(input, input.length - 2, 0);
sink.consume(input, input.length - 1, 0);
sink.consume(input, input.length, 0);
// Invalid chunks
assertConsumeArrayThrowsIOOB(sink, input, -1, 0);
assertConsumeArrayThrowsIOOB(sink, input, -1, 3);
assertConsumeArrayThrowsIOOB(sink, input, 0, input.length + 1);
assertConsumeArrayThrowsIOOB(sink, input, input.length - 2, 4);
assertConsumeArrayThrowsIOOB(sink, input, input.length + 1, 0);
assertConsumeArrayThrowsIOOB(sink, input, input.length + 1, 1);
assertContentsEquals("cdea", sink);
}
}
@Test
public void testConsumeFromByteBuffer() throws Exception {
try (CloseableWithDataSink<T> c = createDataSink()) {
T sink = c.getDataSink();
ByteBuffer input = ByteBuffer.wrap("abcdefg".getBytes(StandardCharsets.UTF_8));
input.position(2);
input.limit(5);
sink.consume(input); // "cde"
assertEquals(5, input.position());
assertEquals(5, input.limit());
input.position(0);
input.limit(1);
sink.consume(input); // "a"
assertContentsEquals("cdea", sink);
// Empty input
sink.consume(input);
assertContentsEquals("cdea", sink);
// ByteBuffer which isn't backed by a byte[]
input = ByteBuffer.allocateDirect(2);
input.put((byte) 'X');
input.put((byte) 'Z');
input.flip();
sink.consume(input);
assertContentsEquals("cdeaXZ", sink);
assertEquals(2, input.position());
assertEquals(2, input.limit());
// Empty input
sink.consume(input);
assertContentsEquals("cdeaXZ", sink);
}
}
/**
* Returns the contents of the provided buffer as a string. The buffer's position and limit
* remain unchanged.
*/
private static String toString(ByteBuffer buf) {
return DataSourceTestBase.toString(buf);
}
private void assertContentsEquals(String expectedContents, T sink) throws IOException {
ByteBuffer actual = getContents(sink);
assertEquals(expectedContents, toString(actual));
}
private static void assertConsumeArrayThrowsIOOB(
DataSink sink, byte[] arr, int offset, int length) throws IOException {
try {
sink.consume(arr, offset, length);
fail();
} catch (IndexOutOfBoundsException expected) {}
}
public static class CloseableWithDataSink<T extends DataSink> implements Closeable {
private final T mDataSink;
private final Closeable mCloseable;
private CloseableWithDataSink(T dataSink, Closeable closeable) {
mDataSink = dataSink;
mCloseable = closeable;
}
public static <T extends DataSink> CloseableWithDataSink<T> of(T dataSink) {
return new CloseableWithDataSink<>(dataSink, null);
}
public static <T extends DataSink> CloseableWithDataSink<T> of(
T dataSink, Closeable closeable) {
return new CloseableWithDataSink<>(dataSink, closeable);
}
public T getDataSink() {
return mDataSink;
}
public Closeable getCloseable() {
return mCloseable;
}
@Override
public void close() throws IOException {
if (mCloseable != null) {
mCloseable.close();
}
}
}
}