// Copyright 2009 Google Inc. All Rights Reserved.
//
// 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.google.enterprise.connector.common;
import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.zip.Deflater;
/**
* Test for CompressedFilterInputStream
*/
public class CompressedFilterInputStreamTest extends TestCase {
// Size of CompressedFilterInputStream buffer for testing.
private static final int BUFF_SIZE = 8192;
private static byte[] emptyInput = new byte[0];
private static byte[] tinyInput = new byte[]{'a'};
private static byte[] smallInput = new byte[]{ 'a', 'b', 'c', 'd' };
private static byte[] mediumInput =
( " Google's indices consist of information that has been"
+ " identified, indexed and compiled through an automated"
+ " process with no advance review by human beings. Given"
+ " the enormous volume of web site information added,"
+ " deleted, and changed on a frequent basis, Google cannot"
+ " and does not screen anything made available through its"
+ " indices. For each web site reflected in Google's"
+ " indices, if either (i) a site owner restricts access to"
+ " his or her web site or (ii) a site is taken down from"
+ " the web, then, upon receipt of a request by the site"
+ " owner or a third party in the second instance, Google"
+ " would consider on a case-by-case basis requests to"
+ " remove the link to that site from its indices. However,"
+ " if the operator of the site does not take steps to"
+ " prevent it, the automatic facilities used to create"
+ " the indices are likely to find that site and index it"
+ " again in a relatively short amount of time.").getBytes();
private static byte[] largeInput;
private static byte[] bigInput;
private static byte[] emptyExpected;
private static byte[] tinyExpected;
private static byte[] smallExpected;
private static byte[] mediumExpected;
private static byte[] largeExpected;
private static byte[] bigExpected;
@Override
protected void setUp() throws Exception {
// Make an input exactly the size of the CompressedFilterInputStream
// internal buffer.
int len = BUFF_SIZE;
largeInput = new byte[len];
for (int i = 0; i < len; i++) {
largeInput[i] = (byte) Math.round(Math.random());
}
// Make an input larger than the CompressedFilterInputStream
// internal buffer. Large enough to test first full buffer,
// subsequent full buffer(s), final partial buffer.
len = 2 * BUFF_SIZE + 69;
bigInput = new byte[len];
for (int i = 0; i < len; i++) {
bigInput[i] = (byte) Math.round(Math.random());
}
// Precalculate the expected results.
emptyExpected = compress(emptyInput);
tinyExpected = compress(tinyInput);
smallExpected = compress(smallInput);
mediumExpected = compress(mediumInput);
largeExpected = compress(largeInput);
bigExpected = compress(bigInput);
input = mediumInput;
expect = mediumExpected;
}
/* Return a compressed version of the input. */
private static byte[] compress(byte[] input) {
Deflater deflater = new Deflater();
byte[] compressed = new byte[Math.max(BUFF_SIZE, input.length * 4)];
deflater.setInput(input);
deflater.finish();
int len = deflater.deflate(compressed);
assertTrue(deflater.finished());
deflater.end();
// Return a byte[] that is exactly the right size.
byte[] output = new byte[len];
System.arraycopy(compressed, 0, output, 0, len);
return output;
}
/* These are extremely useful when debugging these tests.
private static String toHex(byte[] input) {
return toHex(input, 0, input.length);
}
private static String toHex(byte[] input, int off, int len) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < len ; i++) {
byte c = input[off + i];
Base16.upperCase().encode(c, buf);
buf.append(' ');
}
return(buf.toString().trim());
}
*/
/**
* Test read() one byte.
*/
public void testRead() throws IOException {
checkRead(emptyInput, emptyExpected);
checkRead(tinyInput, tinyExpected);
checkRead(smallInput, smallExpected);
checkRead(mediumInput, mediumExpected);
checkRead(largeInput, largeExpected);
checkRead(bigInput, bigExpected);
}
private void checkRead(byte[] input, byte[] expected) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(input);
CompressedFilterInputStream is =
new CompressedFilterInputStream(bais, BUFF_SIZE);
int val = -1;
byte[] result = new byte[expected.length];
int index = 0;
while (-1 != (val = is.read())) {
result[index++] = (byte) val;
}
is.close();
assertEquals(expected.length, index);
assertTrue(Arrays.equals(expected, result));
}
/**
* Test read(byte[] dest, int off, int len);
*/
public void testReadArray() throws IOException {
checkReadArray(emptyInput, emptyExpected);
checkReadArray(tinyInput, tinyExpected);
checkReadArray(smallInput, smallExpected);
checkReadArray(mediumInput, mediumExpected);
checkReadArray(largeInput, largeExpected);
checkReadArray(bigInput, bigExpected);
}
private void checkReadArray(byte[] input, byte[] expected) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(input);
CompressedFilterInputStream is =
new CompressedFilterInputStream(bais, BUFF_SIZE);
byte[] result = new byte[expected.length];
int total = 0;
int val;
while (-1 != (val = is.read(result, total, result.length - total))) {
total += val;
}
is.close();
assertEquals(expected.length, total);
assertTrue(Arrays.equals(expected, result));
}
/**
* ByteArrayInputStream that returns a single byte at a time even if you
* request more.
*/
private class SingleByteArrayInputStream extends ByteArrayInputStream {
public SingleByteArrayInputStream(byte[] bytes) {
super(bytes);
}
@Override
public int read(byte[] b, int off, int len) {
int byteValue = read();
if (-1 == byteValue) {
return -1;
} else {
b[off] = (byte) byteValue;
return 1;
}
}
}
/**
* Test that this stream works even if the underlying stream does not return
* bytes in multiples of 3.
*/
public void testBug243976() throws IOException {
byte[] input = mediumInput;
byte[] expected = mediumExpected;
SingleByteArrayInputStream bais = new SingleByteArrayInputStream(input);
CompressedFilterInputStream is = new CompressedFilterInputStream(bais);
int val;
byte[] result = new byte[expected.length];
int index = 0;
while (-1 != (val = is.read())) {
result[index] = (byte) val;
index++;
}
assertTrue(Arrays.equals(expected, result));
}
private static byte[] input;
private static byte[] expect;
/* Test read(byte[]) interface. */
public void testReadByteArray() throws Exception {
byteArrayRead(1);
byteArrayRead(2);
byteArrayRead(3);
byteArrayRead(4);
byteArrayRead(5);
byteArrayRead(6);
byteArrayRead(7);
byteArrayRead(8);
byteArrayRead(16);
byteArrayRead(23);
byteArrayRead(64);
byteArrayRead(expect.length);
byteArrayRead(((expect.length + 3)/4)*4);
byteArrayRead(expect.length - 1);
byteArrayRead(2048);
}
/* Test read(byte[], off, len) interface. */
public void testReadByteArrayWithOffLen() throws Exception {
byteArrayRead2(1);
byteArrayRead2(2);
byteArrayRead2(3);
byteArrayRead2(4);
byteArrayRead2(5);
byteArrayRead2(6);
byteArrayRead2(7);
byteArrayRead2(8);
byteArrayRead2(16);
byteArrayRead2(23);
byteArrayRead2(64);
byteArrayRead2(expect.length);
byteArrayRead2(((expect.length + 3)/4)*4);
byteArrayRead2(expect.length - 1);
byteArrayRead2(2048);
}
/* Test read(byte[], off, len) interface where off != 0. */
public void testReadByteArrayWithOffset() throws Exception {
byteArrayRead3(1);
byteArrayRead3(2);
byteArrayRead3(3);
byteArrayRead3(4);
byteArrayRead3(5);
byteArrayRead3(6);
byteArrayRead3(7);
byteArrayRead3(8);
byteArrayRead3(16);
byteArrayRead3(23);
byteArrayRead3(64);
byteArrayRead3(expect.length);
byteArrayRead3(((expect.length + 3)/4)*4);
byteArrayRead3(expect.length - 1);
byteArrayRead3(2048);
}
/* Test use of mixed read() and read(byte[], off, len) interface. */
public void testReadWithReadByteArray() throws Exception {
byteArrayRead4(1, 1);
byteArrayRead4(2, 1);
byteArrayRead4(3, 1);
byteArrayRead4(4, 1);
byteArrayRead4(5, 1);
byteArrayRead4(6, 1);
byteArrayRead4(7, 1);
byteArrayRead4(8, 1);
byteArrayRead4(16, 1);
byteArrayRead4(23, 1);
byteArrayRead4(64, 1);
byteArrayRead4(expect.length, 1);
byteArrayRead4(((expect.length + 3)/4)*4, 1);
byteArrayRead4(expect.length - 1, 1);
byteArrayRead4(2048, 1);
byteArrayRead4(1, 2);
byteArrayRead4(2, 2);
byteArrayRead4(3, 2);
byteArrayRead4(4, 2);
byteArrayRead4(5, 2);
byteArrayRead4(6, 2);
byteArrayRead4(7, 2);
byteArrayRead4(8, 2);
byteArrayRead4(16, 2);
byteArrayRead4(23, 2);
byteArrayRead4(64, 2);
byteArrayRead4(expect.length, 2);
byteArrayRead4(((expect.length + 3)/4)*4, 2);
byteArrayRead4(expect.length - 1, 2);
byteArrayRead4(2048, 2);
byteArrayRead4(1, 3);
byteArrayRead4(2, 3);
byteArrayRead4(3, 3);
byteArrayRead4(4, 3);
byteArrayRead4(5, 3);
byteArrayRead4(6, 3);
byteArrayRead4(7, 3);
byteArrayRead4(8, 3);
byteArrayRead4(16, 3);
byteArrayRead4(23, 3);
byteArrayRead4(64, 3);
byteArrayRead4(expect.length, 3);
byteArrayRead4(((expect.length + 3)/4)*4, 3);
byteArrayRead4(expect.length - 1, 3);
byteArrayRead4(2048, 3);
byteArrayRead4(1, 4);
byteArrayRead4(2, 4);
byteArrayRead4(3, 4);
byteArrayRead4(4, 4);
byteArrayRead4(5, 4);
byteArrayRead4(6, 4);
byteArrayRead4(7, 4);
byteArrayRead4(8, 4);
byteArrayRead4(16, 4);
byteArrayRead4(23, 4);
byteArrayRead4(64, 4);
byteArrayRead4(expect.length, 4);
byteArrayRead4(((expect.length + 3)/4)*4, 4);
byteArrayRead4(expect.length - 1, 4);
byteArrayRead4(2048, 4);
}
/* Test read(byte[]) interface. */
public void byteArrayRead(int buffsize) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(input);
CompressedFilterInputStream is = new CompressedFilterInputStream(bais);
byte[] buff = new byte[buffsize];
byte[] result = new byte[expect.length];
int total = 0;
int val;
while (-1 != (val = is.read(buff))) {
System.arraycopy(buff, 0, result, total, val);
total += val;
}
is.close();
assertTrue(Arrays.equals(result, expect));
}
/* Test read(byte[], off, len) interface. */
public void byteArrayRead2(int buffsize) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(input);
CompressedFilterInputStream is = new CompressedFilterInputStream(bais);
byte[] buff = new byte[buffsize];
byte[] result = new byte[expect.length];
int total = 0;
int val;
while (-1 != (val = is.read(buff, 0, buffsize))) {
System.arraycopy(buff, 0, result, total, val);
total += val;
}
is.close();
assertTrue(Arrays.equals(result, expect));
}
/* Test read(byte[], off, len) interface, where off != 0. */
public void byteArrayRead3(int buffsize) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(input);
CompressedFilterInputStream is = new CompressedFilterInputStream(bais);
byte[] buff = new byte[buffsize + 3];
byte[] result = new byte[expect.length];
int total = 0;
int val;
while (-1 != (val = is.read(buff, 3, buffsize))) {
System.arraycopy(buff, 3, result, total, val);
total += val;
}
is.close();
assertTrue(Arrays.equals(result, expect));
}
/* Test use of mixed read() and read(byte[], off, len) interface. */
public void byteArrayRead4(int buffsize, int readsize) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(input);
CompressedFilterInputStream is = new CompressedFilterInputStream(bais);
byte[] buff = new byte[buffsize];
byte[] result = new byte[expect.length];
int total = 0;
int val;
while ((readsize-- > 0) && (-1 != (val = is.read()))) {
result[total++] = (byte) val;
}
while (-1 != (val = is.read(buff, 0, buffsize))) {
System.arraycopy(buff, 0, result, total, val);
total += val;
}
is.close();
assertTrue(Arrays.equals(result, expect));
}
}