package org.owasp.esapi.crypto;

import static org.junit.Assert.*;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import junit.framework.JUnit4TestAdapter;
import org.junit.Test;
import org.owasp.esapi.crypto.PlainText;

public class PlainTextTest {
	
	private String unicodeStr = "A\u00ea\u00f1\u00fcC";	// I.e., "AêñüC"
	private String altString  = "AêñüC";				// Same as above.
	
	/* NOTE: This test will not work on Windows unless executed under
	 * Eclipse and the file is stored / treated as a UTF-8 encoded file
	 * rather than the Windows native OS encoding of Windows-1252 (aka,
	 * CP-1252). Therefore this test case has a check to not run the test
	 * unless
	 *     unicodeStr.equals(altString)
	 * is true. If not the test is skipped and a message is printed to stderr.
	 * Jim Manico made an attempt to address this (see private email to
	 * kevin.w.wall@gmail.com on 11/26/2009, subject "Re: [OWASP-ESAPI] Unit
	 * Tests Status") to correct this problem by setting some SVN attribute
	 * to standardize all source files to UTF-8, but not all Subversion clients
	 * are either honoring this or perhaps Windows just overrides this. Either
	 * way, this test (which used to be an assertTrue() expression) was
	 * introduced to account for this.
	 */
	@Test
	public final void testUnicodeString() {
	    // These 2 strings are *meant* to be equal. If they are not, please
	    // do *NOT* change the test. It's a Windows thing. Sorry. Change your
	    // OS instead. ;-)
	    if ( ! unicodeStr.equals(altString) ) {
	        System.err.println("Skipping JUnit test case " +
	                           "PlainTextTest.testUnicodeString() on OS " +
	                           System.getProperty("os.name") );
	        return;
	    }
		try {
			byte[] utf8Bytes = unicodeStr.getBytes("UTF-8");
			PlainText pt1 = new PlainText(unicodeStr);
			PlainText pt2 = new PlainText(altString);
			
			assertTrue( pt1.equals(pt1) );   // Equals self
			assertFalse( pt1.equals(null) );
			assertTrue( pt1.equals(pt2) );
			assertFalse( pt1.equals( unicodeStr ) );
			assertTrue( pt1.length() == utf8Bytes.length );
			assertTrue( Arrays.equals(utf8Bytes, pt1.asBytes()) );			
			assertTrue( pt1.hashCode() == unicodeStr.hashCode() );
			
		} catch (UnsupportedEncodingException e) {
			fail("No UTF-8 byte encoding: " + e);
			e.printStackTrace(System.err);
		}
	}
	
	@Test
	public final void testNullCase() {
	    int counter = 0;
	    try {
            byte[] bytes = null;
            PlainText pt = new PlainText(bytes);
            fail("testNullCase(): Expected IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            ;   // Success
        } catch (Exception e) {
            fail("testNullCase(): Caught unexpected exception: " + e);
        }
	}

	@Test
	public final void testEmptyString() {
		PlainText mt  = new PlainText("");
		assertTrue( mt.length() == 0 );
		byte[] ba = mt.asBytes();
		assertTrue( ba != null && ba.length == 0 );
	}

	@Test
	public final void testOverwrite() {
		try {
			byte[] origBytes = unicodeStr.getBytes("UTF-8");
			PlainText pt = new PlainText(origBytes);
			assertTrue( pt.toString().equals(unicodeStr) );
			assertTrue( Arrays.equals(origBytes, pt.asBytes()) );			
			assertTrue( pt.hashCode() == unicodeStr.hashCode() );
			
			int origLen = origBytes.length;

			pt.overwrite();
	        byte[] overwrittenBytes = pt.asBytes();
			assertTrue( overwrittenBytes != null );
			assertFalse( Arrays.equals( origBytes, overwrittenBytes ) );

			// Ensure that ALL the bytes overwritten with '*'.
			int afterLen = overwrittenBytes.length;
			assertTrue( origLen == afterLen );
			int sum = 0;
			for( int i = 0; i < afterLen; i++ ) {
			    if ( overwrittenBytes[i] == '*' ) {
			        sum++;
			    }
			}
			assertTrue( afterLen == sum );
		} catch (UnsupportedEncodingException e) {
			fail("No UTF-8 byte encoding: " + e);
			e.printStackTrace(System.err);
		}
	}

	/**
	 * Run all the test cases in this suite.
	 * This is to allow running from {@code org.owasp.esapi.AllTests} which
	 * uses a JUnit 3 test runner.
	 */
	public static junit.framework.Test suite() {
		return new JUnit4TestAdapter(PlainTextTest.class);
	}
}
