Project: jackrabbit-oak
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more 
 * contributor license agreements.  See the NOTICE file distributed with 
 * this work for additional information regarding copyright ownership. 
 * The ASF licenses this file to You 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.apache.jackrabbit.mk.util; 
 
import junit.framework.TestCase; 
 
import java.io.ByteArrayInputStream; 
import java.io.ByteArrayOutputStream; 
import java.io.EOFException; 
import java.io.FilterInputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import java.util.Random; 
import java.util.concurrent.atomic.AtomicInteger; 
 
/**
 * Test the utility classes. 
 */
 
public class IOUtilsTest extends TestCase { 
 
    public void testReadFully() throws IOException { 
        final Random r = new Random(1); 
        byte[] data = new byte[1000]; 
        final AtomicInteger readCount = new AtomicInteger(); 
        r.nextBytes(data); 
        FilterInputStream in = new FilterInputStream(new ByteArrayInputStream(data)) { 
            public int read(byte[] buffer, int off, int max) throws IOException { 
                readCount.incrementAndGet(); 
                if (r.nextInt(10) == 0) { 
                    return 0
                } 
                return in.read(buffer, off, Math.min(10, max)); 
            } 
        }; 
        in.mark(10000); 
        byte[] test = new byte[1000]; 
 
        // readFully is not supposed to call read when reading 0 bytes 
        assertEquals(0, IOUtils.readFully(in, test, 00)); 
        assertEquals(0, readCount.get()); 
 
        assertEquals(1000, IOUtils.readFully(in, test, 01000)); 
        IOUtilsTest.assertEquals(data, test); 
        test = new byte[1001]; 
        in.reset(); 
        in.mark(10000); 
        assertEquals(1000, IOUtils.readFully(in, test, 01001)); 
        assertEquals(0, IOUtils.readFully(in, test, 00)); 
    } 
 
    public void testSkipFully() throws IOException { 
        final Random r = new Random(1); 
        byte[] data = new byte[1000]; 
        r.nextBytes(data); 
        FilterInputStream in = new FilterInputStream(new ByteArrayInputStream(data)) { 
            public int read(byte[] buffer, int off, int max) throws IOException { 
                return in.read(buffer, off, Math.min(10, max)); 
            } 
        }; 
        in.mark(10000); 
        IOUtils.skipFully(in, 1000); 
        assertEquals(-1, in.read()); 
        in.reset(); 
        try { 
            IOUtils.skipFully(in, 1001); 
            fail(); 
        } catch (EOFException e) { 
            // expected 
        } 
    } 
 
    public void testStringReadWrite() throws IOException { 
        final Random r = new Random(1); 
        for (int i = 0; i < 100000; i += i / 10 + 1) { 
            String s = ""
            for (int j = 0; j < 10; j++) { 
                String p = new String(new char[i]).replace((char0, 'a'); 
                s += p; 
            } 
            ByteArrayOutputStream out = new ByteArrayOutputStream(); 
            IOUtils.writeString(out, s); 
            byte[] data = out.toByteArray(); 
            ByteArrayInputStream in = new ByteArrayInputStream(data) { 
                public int read(byte[] b, int off, int len) { 
                    if (r.nextBoolean()) { 
                        len = r.nextInt(len); 
                    } 
                    return super.read(b, off, len); 
                } 
            }; 
            String t = IOUtils.readString(in); 
            assertEquals(s, t); 
            assertEquals(-1, in.read()); 
        } 
        try { 
            InputStream in = new ByteArrayInputStream(new byte[]{1}); 
            IOUtils.readString(in); 
            fail(); 
        } catch (EOFException e) { 
            // expected 
        } 
    } 
 
    public void testBytesReadWrite() throws IOException { 
        final Random r = new Random(); 
        int iterations = 1000
        while (iterations-- > 0) { 
            int n = Math.abs(r.nextInt()) % 0x40000
            byte[] buf = new byte[n]; 
            r.nextBytes(buf); 
            ByteArrayOutputStream out = new ByteArrayOutputStream(); 
            IOUtils.writeBytes(out, buf); 
            byte[] buf1 = IOUtils.readBytes(new ByteArrayInputStream(out.toByteArray())); 
            assertEquals(buf, buf1); 
        } 
    } 
 
    public void testVarInt() throws IOException { 
        testVarInt(01); 
        testVarInt(0x7f1); 
        testVarInt(0x802); 
        testVarInt(0x3fff2); 
        testVarInt(0x40003); 
        testVarInt(0x1fffff3); 
        testVarInt(0x2000004); 
        testVarInt(0xfffffff4); 
        testVarInt(0x100000005); 
        testVarInt(-15); 
        for (int x = 0; x < 0x20000; x++) { 
            testVarInt(x, 0); 
            testVarInt(Integer.MIN_VALUE + x, 0); 
            testVarInt(Integer.MAX_VALUE - x, 5); 
            testVarInt(0x200000 + x - 1000); 
            testVarInt(0x10000000 + x - 1000); 
        } 
        Random r = new Random(1); 
        for (int i = 0; i < 100000; i++) { 
            testVarInt(r.nextInt(), 0); 
            testVarInt(r.nextInt(10000000), 0); 
        } 
 
        // trailing 0s are never written, but are an alternative way to encode a value 
        InputStream in = new ByteArrayInputStream(new byte[]{(byte0x800}); 
        assertEquals(0, IOUtils.readVarInt(in)); 
        assertEquals(-1, in.read()); 
    } 
 
    public void testVarLong() throws IOException { 
        testVarLong(01); 
        testVarLong(0x7f1); 
        testVarLong(0x802); 
        testVarLong(0x3fff2); 
        testVarLong(0x40003); 
        testVarLong(0x1fffff3); 
        testVarLong(0x2000004); 
        testVarLong(0xfffffff4); 
        testVarLong(0x100000005); 
        testVarLong(0x1fffffffL5); 
        testVarLong(0x2000000000L6); 
        testVarLong(0x3ffffffffffL6); 
        testVarLong(0x40000000000L7); 
        testVarLong(0x1ffffffffffffL7); 
        testVarLong(0x2000000000000L8); 
        testVarLong(0xffffffffffffffL8); 
        testVarLong(0x100000000000000L9); 
        testVarLong(-110); 
        for (int x = 0; x < 0x20000; x++) { 
            testVarLong(x, 0); 
            testVarLong(Long.MIN_VALUE + x, 0); 
            testVarLong(Long.MAX_VALUE - x, 9); 
            testVarLong(0x200000 + x - 1000); 
            testVarLong(0x10000000 + x - 1000); 
        } 
        Random r = new Random(1); 
        for (int i = 0; i < 100000; i++) { 
            testVarLong(r.nextLong(), 0); 
            testVarLong(r.nextInt(Integer.MAX_VALUE), 0); 
        } 
 
        // trailing 0s are never written, but are an alternative way to encode a value 
        InputStream in = new ByteArrayInputStream(new byte[]{(byte0x800}); 
        assertEquals(0, IOUtils.readVarLong(in)); 
        assertEquals(-1, in.read()); 
    } 
 
    public void testVarEOF() throws IOException { 
        try { 
            IOUtils.readVarInt(new ByteArrayInputStream(new byte[0])); 
            fail(); 
        } catch (EOFException e) { 
            // expected 
        } 
        try { 
            IOUtils.readVarLong(new ByteArrayInputStream(new byte[0])); 
            fail(); 
        } catch (EOFException e) { 
            // expected 
        } 
    } 
 
    private static void testVarInt(int x, int expectedLen) throws IOException { 
        ByteArrayOutputStream out = new ByteArrayOutputStream(); 
        IOUtils.writeVarInt(out, x); 
        byte[] data = out.toByteArray(); 
        assertTrue(data.length <= 5); 
        if (expectedLen > 0) { 
            assertEquals(expectedLen, data.length); 
        } 
        ByteArrayInputStream in = new ByteArrayInputStream(data); 
        int x2 = IOUtils.readVarInt(in); 
        assertEquals(x, x2); 
        assertEquals(-1, in.read()); 
    } 
 
    private static void testVarLong(long x, int expectedLen) throws IOException { 
        ByteArrayOutputStream out = new ByteArrayOutputStream(); 
        IOUtils.writeVarLong(out, x); 
        byte[] data = out.toByteArray(); 
        assertTrue(data.length <= 10); 
        if (expectedLen > 0) { 
            assertEquals(expectedLen, data.length); 
        } 
        ByteArrayInputStream in = new ByteArrayInputStream(data); 
        long x2 = IOUtils.readVarLong(in); 
        assertEquals(x, x2); 
        assertEquals(-1, in.read()); 
    } 
 
    public static void assertEquals(byte[] expected, byte[] got) { 
        assertEquals(expected.length, got.length); 
        for (int i = 0; i < got.length; i++) { 
            assertEquals(expected[i], got[i]); 
        } 
    } 
 
}