/* * 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; }