/* 
 * E-XML Library:  For XML, XML-RPC, HTTP, and related.
 * Copyright (C) 2002-2008  Elias Ross
 * 
 * genman@noderunner.net
 * http://noderunner.net/~genman
 * 
 * 1025 NE 73RD ST
 * SEATTLE WA 98115
 * USA
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * $Id$
 */

package net.noderunner.http;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;

/**
 * This class is for unit testing ChunkedOutputStream and ChunkedInputStream.
 *
 * @author Elias Ross
 * @version 1.0
 */
public class ChunkStreamTest
	extends junit.framework.TestCase
{
	public ChunkStreamTest(String name) {
		super(name);
	}

	static final byte stuff[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' };

	public void testWriteRead()
		throws IOException
	{
		//System.out.println("testWriteRead");
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ChunkedOutputStream cos = new ChunkedOutputStream(bos);
		for (int i = 1; i < stuff.length; i++)
			cos.write(stuff, 0, i);
		cos.doneOutput();
		cos.doneOutput(); // shouldn't matter
		// read in
		byte stuffin[] = bos.toByteArray();
		InputStream is = new ByteArrayInputStream(stuffin);
		ChunkedInputStream cis = new ChunkedInputStream(is);
		for (int i = 1; i < stuff.length; i++) {
			int len = cis.read(stuffin, 0, i);
			assertEquals("len", i, len);
			for (int j = 0; j < len; j++)
				assertEquals("position", stuff[j], stuffin[j]);
		}
		// EOF
		assertEquals(-1, cis.read());
	}

	public void testWriteRead2()
		throws IOException
	{
		//System.out.println("testWriteRead2");
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ChunkedOutputStream cos = new ChunkedOutputStream(bos);
		for (int i = 0; i < stuff.length; i++)
			cos.write(stuff[i]);
		cos.close();
		// read in
		InputStream is = new ByteArrayInputStream(bos.toByteArray());
		ChunkedInputStream cis = new ChunkedInputStream(is);
		for (int i = 0; i < stuff.length; i++) {
			int what = cis.read();
			assertEquals("what " + i, (char)stuff[i], (char)what);
		}
		// EOF
		assertEquals(-1, cis.read());
		assertEquals(-1, cis.read(new byte[1], 0, 1));
	}

	public void testReadBi()
		throws IOException
	{
		//System.out.println("testReadBi");
		int    ints[] = { 0x0Fe, 1, 45, 7, 55, 1, 0x1ceface1, 0x1234, 0x00001, 0xABCD, 0xEF, 0x12};
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ChunkedOutputStream cos = new ChunkedOutputStream(bos);
		for (int i = 0; i < ints.length; i++)
			cos.writeChunkLen(ints[i]);
		byte buf[] = bos.toByteArray();
		cos.close();
		// read
		InputStream is = new ByteArrayInputStream(buf);
		ChunkedInputStream cis = new ChunkedInputStream(is);
		for (int i = 0; i < ints.length; i++) {
			int len = cis.readLengthFromStream();
			assertEquals("Read Hex Len", ints[i], len);
		}
		// at eof
		try {
			cis.readLengthFromStream();
			fail("bad length");
		} catch (IOException e) {
		}
	}

	public void testReadHex()
		throws IOException
	{
		String hexs[] = { "00Fe", "1234", "000001", "ABCD", "EF" };
		int    ints[] = { 0x0Fe, 0x1234, 0x00001, 0xABCD, 0xEF   };
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		OutputStreamWriter osw = new OutputStreamWriter(bos);
		for (int i = 0; i < hexs.length; i++) {
			osw.write(hexs[i]);
			osw.write("\r\n");
		}
		osw.write("\r\n");
		osw.write("GHJH\r\n");
		osw.close();
		byte  buf[] = bos.toByteArray();
		// read in chunks
		InputStream is = new ByteArrayInputStream(buf);
		ChunkedInputStream cis = new ChunkedInputStream(is);
		for (int i = 0; i < hexs.length; i++) {
			int len = cis.readLengthFromStream();
			assertEquals("Read Hex Len", ints[i], len);
		}
		cis.readChunkEnd();
		try {
			//System.out.println("testReadHex readLengthFromStream");
			cis.readLengthFromStream();
			fail("bad length");
		} catch (IOException e) {
		}
		cis.close();
	}

	public void testCoverage()
		throws IOException
	{
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ChunkedOutputStream cos = new ChunkedOutputStream(bos);
		cos.toString();
		cos.write(new byte[] { 1, 2 } );
		byte stuffin[] = bos.toByteArray();

		InputStream is = new ByteArrayInputStream(stuffin);
		ChunkedInputStream cis = new ChunkedInputStream(is);
		cis.toString();
		assertEquals(1, cis.read());
		assertEquals(2, cis.read());
	}
	
	public void testChunkExtension()
		throws IOException
	{
		String s = 
			"01;an extension\r\nx\r\n" +
			"00\r\nheader: value\r\n\r\n";
		InputStream is = new ByteArrayInputStream(s.getBytes());
		ChunkedInputStream cis = new ChunkedInputStream(is);
		assertEquals('x', cis.read());
		assertEquals(null, cis.getEntityHeaders());
		assertEquals(false, cis.isEndChunk());
		assertEquals(-1, cis.read());
		assertEquals(true, cis.isEndChunk());
		assertEquals("value", cis.getEntityHeaders().getFieldContent("header"));
		
		s = "00\r\nheader: val\r\n \r\n";
		is = new ByteArrayInputStream(s.getBytes());
		cis = new ChunkedInputStream(is);
		try {
    		cis.read();
    		fail("no CRLF");
		} catch (IOException e) {}
		
		s = 
			"01\r\nx\r\n" +
			"00\r\nan entity-header\r \n\r\n";
		is = new ByteArrayInputStream(s.getBytes());
		cis = new ChunkedInputStream(is);
		cis.read();
		try {
    		cis.read();
    		fail("no CRLF");
		} catch (IOException e) {}
	}

	public void testBadEnd()
		throws IOException
	{
		InputStream is = new ByteArrayInputStream(new byte[] { '1', '\n', '\n'} );
		ChunkedInputStream cis = new ChunkedInputStream(is);
		try {
			cis.read();
			fail("bad seq");
		} catch (IOException e) { }

		is = new ByteArrayInputStream(new byte[] { 'F', '\r', '\n' } );
		cis = new ChunkedInputStream(is);
		assertEquals(-1, cis.read(new byte[100], 0, 100));

		is = new ByteArrayInputStream(new byte[] { 'G', '\r', '\n'} );
		cis = new ChunkedInputStream(is);
		try {
			cis.read();
			fail("bad seq");
		} catch (IOException e) { }

		is = new ByteArrayInputStream(new byte[] { 'g', '\r', '\n'} );
		cis = new ChunkedInputStream(is);
		try {
			cis.read();
			fail("bad seq");
		} catch (IOException e) { }

		is = new ByteArrayInputStream("a\r\n01234567890\n\r".getBytes());
		cis = new ChunkedInputStream(is);
		assertEquals('0', cis.read());

		is = new ByteArrayInputStream(new byte[] { '1', '\r', '\r'} );
		cis = new ChunkedInputStream(is);
		try {
			cis.read();
			fail("bad seq");
		} catch (IOException e) { }

		is = new ByteArrayInputStream("01\r\nA\n\r".getBytes());
		cis = new ChunkedInputStream(is);
		cis.read();
		try {
			cis.read();
			fail("bad seq");
		} catch (IOException e) { }

		is = new ByteArrayInputStream(new byte[] { '1', '\r', '\n', 'a', '\r', '?' } );
		cis = new ChunkedInputStream(is);
		cis.read();
		try {
			cis.read();
			fail("bad seq");
		} catch (IOException e) { }

		is = new ByteArrayInputStream(new byte[] { '1', ' ', '\r', '\n', 'a'} );
		cis = new ChunkedInputStream(is);
    assertEquals("Space in length, not in spec, but apache 1.3.29 does it", 'a', cis.read());
	}

	public void testParam()
		throws IOException
	{
		try {
			new ChunkedInputStream(null);
			fail("null input");
		} catch (IllegalArgumentException e) {
		}
		try {
			new ChunkedOutputStream(null);
			fail("null input");
		} catch (IllegalArgumentException e) {
		}
		try {
			ChunkedOutputStream os = new ChunkedOutputStream(new ByteArrayOutputStream());
			os.doneOutput();
			os.write('a');
			fail("already wrote last chunk");
		} catch (IllegalStateException e) {
		}
		try {
			ChunkedOutputStream os = new ChunkedOutputStream(new ByteArrayOutputStream());
			os.doneOutput();
			os.write(stuff, 0, stuff.length);
			fail("already wrote last chunk");
		} catch (IllegalStateException e) {
		}
	}

}
