/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.remoting;
import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Random;
import org.apache.commons.codec.binary.Base64;
/**
* @author Kohsuke Kawaguchi
*/
public class BinarySafeStreamTest extends TestCase {
public void test1() throws IOException {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
OutputStream o = BinarySafeStream.wrap(buf);
byte[] data = "Sending some data to make sure it's encoded".getBytes("UTF-8");
o.write(data);
o.close();
InputStream in = BinarySafeStream.wrap(new ByteArrayInputStream(buf.toByteArray()));
for (byte b : data) {
int ch = in.read();
assertEquals(b, ch);
}
assertEquals(-1,in.read());
}
public void testSingleWrite() throws IOException {
byte[] ds = getDataSet(65536);
String master = new String(Base64.encodeBase64(ds));
ByteArrayOutputStream buf = new ByteArrayOutputStream();
OutputStream o = BinarySafeStream.wrap(buf);
o.write(ds,0,ds.length);
o.close();
assertEquals(buf.toString(),master);
}
public void testChunkedWrites() throws IOException {
byte[] ds = getDataSet(65536);
String master = new String(Base64.encodeBase64(ds));
Random r = new Random(0);
for( int i=0; i<16; i++) {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
OutputStream o = BinarySafeStream.wrap(buf);
randomCopy(r,new ByteArrayInputStream(ds),o,false);
assertEquals(buf.toString(),master);
}
}
public void testRoundtripNoFlush() throws IOException {
_testRoundtrip(false);
}
public void testRoundtripFlush() throws IOException {
_testRoundtrip(true);
}
private void _testRoundtrip(boolean flush) throws IOException {
byte[] dataSet = getDataSet(65536);
Random r = new Random(0);
for(int i=0; i<16; i++) {
if(dump)
System.out.println("test started");
ByteArrayOutputStream buf = new ByteArrayOutputStream();
randomCopy(r,new ByteArrayInputStream(dataSet), BinarySafeStream.wrap(buf), flush);
decodeByMaster(buf.toString(),dataSet);
if(dump)
System.out.println("------");
ByteArrayOutputStream dst = new ByteArrayOutputStream();
randomCopy(r,BinarySafeStream.wrap(new ByteArrayInputStream(buf.toByteArray())), dst,flush);
byte[] result = dst.toByteArray();
if(!Arrays.equals(dataSet, result)) {
String msg = print(result, 0, result.length);
for( int j=0; j<result.length; j++ )
assertEquals("offset "+j+" at "+msg,result[j],dataSet[j]);
fail(msg);
}
if(dump)
System.out.println("------");
}
}
/**
* Decodes by the JDK base64 code and make sure the encoded string looks correct.
*/
private void decodeByMaster(String s, byte[] dataSet) throws IOException {
int ptr=0;
for( int i=0; i<s.length(); i+=4 ) {
byte[] buf = Base64.decodeBase64(s.substring(i,i+4).getBytes());
for (int j = 0; j < buf.length; j++) {
if(buf[j]!=dataSet[ptr])
fail("encoding error at offset "+ptr);
ptr++;
}
}
}
/**
* Creates a test data set.
*/
private byte[] getDataSet(int len) {
byte[] dataSet = new byte[len];
for( int i=0; i<dataSet.length; i++ )
dataSet[i] = (byte)i;
return dataSet;
}
private void randomCopy(Random r, InputStream in, OutputStream out, boolean randomFlash) throws IOException {
try {
while(true) {
switch(r.nextInt(3)) {
case 0:
int ch = in.read();
if(dump)
System.out.println("read1("+ch+')');
assertTrue(255>=ch && ch>=-1); // make sure the range is [-1,255]
if(ch==-1)
return;
out.write(ch);
break;
case 1:
int start = r.nextInt(16);
int chunk = r.nextInt(16);
int trail = r.nextInt(16);
byte[] tmp = new byte[start+chunk+trail];
int len = in.read(tmp, start, chunk);
if(dump)
System.out.println("read2("+print(tmp,start,len)+",len="+len+",chunk="+chunk+")");
if(len==-1)
return;
// check extra data corruption
for( int i=0; i<start; i++)
assertEquals(tmp[i],0);
for( int i=0; i<trail; i++)
assertEquals(tmp[start+chunk+i],0);
out.write(tmp,start,len);
break;
case 2:
len = r.nextInt(16);
tmp = new byte[len];
len = in.read(tmp);
if(dump)
System.out.println("read3("+print(tmp,0,len)+",len="+len+')');
if(len==-1)
return;
// obtain the array of the exact size
byte[] n = new byte[len];
System.arraycopy(tmp,0,n,0,len);
out.write(n);
}
if(randomFlash && r.nextInt(8)==0)
out.flush();
}
} finally {
out.close();
}
}
private static String print(byte[] buf, int start, int len) {
StringBuilder out = new StringBuilder();
out.append('{');
for (int i = 0; i < len; i++) {
byte b = buf[i+start];
if(i>0) out.append(',');
out.append(((int)b)&0xFF);
}
return out.append('}').toString();
}
private static final boolean dump = false;
}