Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.apksig.util;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.fail;
     21 
     22 import java.io.Closeable;
     23 import java.io.IOException;
     24 import java.nio.ByteBuffer;
     25 import java.nio.charset.StandardCharsets;
     26 import org.junit.Test;
     27 
     28 /**
     29  * Base class for testing implementations of {@link DataSink}. This class tests the contract of
     30  * {@code DataSink}.
     31  *
     32  * <p>To subclass, provide an implementation of {@link #createDataSink()} which returns the
     33  * implementation of {@code DataSink} you want to test.
     34  */
     35 public abstract class DataSinkTestBase<T extends DataSink> {
     36     /**
     37      * Returns a new {@link DataSink}.
     38      */
     39     protected abstract CloseableWithDataSink<T> createDataSink() throws IOException;
     40 
     41     /**
     42      * Returns the contents of the data sink.
     43      */
     44     protected abstract ByteBuffer getContents(T dataSink) throws IOException;
     45 
     46     @Test
     47     public void testConsumeFromArray() throws Exception {
     48         try (CloseableWithDataSink<T> c = createDataSink()) {
     49             T sink = c.getDataSink();
     50             byte[] input = "abcdefg".getBytes(StandardCharsets.UTF_8);
     51             sink.consume(input, 2, 3); // "cde"
     52             sink.consume(input, 0, 1); // "a"
     53             assertContentsEquals("cdea", sink);
     54 
     55             // Zero-length chunks
     56             sink.consume(input, 0, 0);
     57             sink.consume(input, 1, 0);
     58             sink.consume(input, input.length - 2, 0);
     59             sink.consume(input, input.length - 1, 0);
     60             sink.consume(input, input.length, 0);
     61 
     62             // Invalid chunks
     63             assertConsumeArrayThrowsIOOB(sink, input, -1, 0);
     64             assertConsumeArrayThrowsIOOB(sink, input, -1, 3);
     65             assertConsumeArrayThrowsIOOB(sink, input, 0, input.length + 1);
     66             assertConsumeArrayThrowsIOOB(sink, input, input.length - 2, 4);
     67             assertConsumeArrayThrowsIOOB(sink, input, input.length + 1, 0);
     68             assertConsumeArrayThrowsIOOB(sink, input, input.length + 1, 1);
     69 
     70             assertContentsEquals("cdea", sink);
     71         }
     72     }
     73 
     74     @Test
     75     public void testConsumeFromByteBuffer() throws Exception {
     76         try (CloseableWithDataSink<T> c = createDataSink()) {
     77             T sink = c.getDataSink();
     78             ByteBuffer input = ByteBuffer.wrap("abcdefg".getBytes(StandardCharsets.UTF_8));
     79             input.position(2);
     80             input.limit(5);
     81             sink.consume(input); // "cde"
     82             assertEquals(5, input.position());
     83             assertEquals(5, input.limit());
     84 
     85             input.position(0);
     86             input.limit(1);
     87             sink.consume(input); // "a"
     88             assertContentsEquals("cdea", sink);
     89 
     90             // Empty input
     91             sink.consume(input);
     92             assertContentsEquals("cdea", sink);
     93 
     94             // ByteBuffer which isn't backed by a byte[]
     95             input = ByteBuffer.allocateDirect(2);
     96             input.put((byte) 'X');
     97             input.put((byte) 'Z');
     98             input.flip();
     99             sink.consume(input);
    100 
    101             assertContentsEquals("cdeaXZ", sink);
    102             assertEquals(2, input.position());
    103             assertEquals(2, input.limit());
    104 
    105             // Empty input
    106             sink.consume(input);
    107             assertContentsEquals("cdeaXZ", sink);
    108         }
    109     }
    110 
    111     /**
    112      * Returns the contents of the provided buffer as a string. The buffer's position and limit
    113      * remain unchanged.
    114      */
    115     private static String toString(ByteBuffer buf) {
    116         return DataSourceTestBase.toString(buf);
    117     }
    118 
    119     private void assertContentsEquals(String expectedContents, T sink) throws IOException {
    120         ByteBuffer actual = getContents(sink);
    121         assertEquals(expectedContents, toString(actual));
    122     }
    123 
    124     private static void assertConsumeArrayThrowsIOOB(
    125             DataSink sink, byte[] arr, int offset, int length) throws IOException {
    126         try {
    127             sink.consume(arr, offset, length);
    128             fail();
    129         } catch (IndexOutOfBoundsException expected) {}
    130     }
    131 
    132     public static class CloseableWithDataSink<T extends DataSink> implements Closeable {
    133         private final T mDataSink;
    134         private final Closeable mCloseable;
    135 
    136         private CloseableWithDataSink(T dataSink, Closeable closeable) {
    137             mDataSink = dataSink;
    138             mCloseable = closeable;
    139         }
    140 
    141         public static <T extends DataSink> CloseableWithDataSink<T> of(T dataSink) {
    142             return new CloseableWithDataSink<>(dataSink, null);
    143         }
    144 
    145         public static <T extends DataSink> CloseableWithDataSink<T> of(
    146                 T dataSink, Closeable closeable) {
    147             return new CloseableWithDataSink<>(dataSink, closeable);
    148         }
    149 
    150         public T getDataSink() {
    151             return mDataSink;
    152         }
    153 
    154         public Closeable getCloseable() {
    155             return mCloseable;
    156         }
    157 
    158         @Override
    159         public void close() throws IOException {
    160             if (mCloseable != null) {
    161                 mCloseable.close();
    162             }
    163         }
    164     }
    165 }
    166