package nxm.sys.test.junit.lib;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import nxm.sys.lib.Midas;
import nxm.sys.lib.Shell;
import nxm.sys.lib.Stack;
import nxm.sys.test.junit.NMTestCase;

/** This will test the Stack class.
 *  Initial testing is for this JDK warning
 *    warning: [serial] non-transient instance field of a serializable class declared with an array having a non-serializable base component type Object
 *    WARN:   private Object[] objects;
 *  @author  Jeanette McCall
 *  @since   NeXtMidas 4.1.1
 */
public final class StackTest extends NMTestCase {
  /** public constructor to allow running this test case directly with JUnit. */
  public StackTest (String methodName) { this(methodName, null); }

  /** Creates a new instance of the class.
      @param methodName The name of the method being called.
      @param midas      The midas context to use when running the tests.
   */
  public StackTest (String methodName, Midas midas) {
    super(methodName, midas);
  }

  /** Test the case where Stack serialization will succeed */
  public void testSerializable () {
    Stack history = new Stack();
    Object[] values = {"first", "second", "third"};
    history.push(values[2]);
    history.push(values[1]);
    history.push(values[0]);
    File ramFile = null;
    try {
      ramFile = File.createTempFile("stackserializationtest", "temp");
      FileOutputStream fos = new FileOutputStream(ramFile);
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(history);
      oos.flush();
      fos.close();
      FileInputStream fis = new FileInputStream(ramFile);
      ObjectInputStream ois = new ObjectInputStream(fis);
      Stack    hist       = (Stack)ois.readObject();
      fis.close();
      Object peekobj = hist.peek(-1);
      String peekstr;
      int expIdx = 0;
      while (peekobj != null) {
        peekstr = (String)peekobj;
        assertEquals("reserialized Stack entry "+expIdx, values[expIdx], peekstr);
        expIdx++;
        peekobj = hist.peek(-1);
      }
    }
    catch (Exception e) {
      fail("Error during serialization:"+e);
    }
    finally {
      ramFile.delete();
    }
  }

  /** Test the case where Stack serialization will fail */
  public void testUnserializable () {
    Stack history = new Stack();
    CustomObject coTwo = new CustomObject(Double.valueOf(2), "two", "Two");
    CustomObject coOne = new CustomObject(Double.valueOf(1), "one", "One");
    CustomObject coZero = new CustomObject(Double.valueOf(0), "zero", "Zero");
    Object[] values = new Object[] {coZero, coOne, coTwo};
    history.push(values[2]);
    history.push(values[1]);
    history.push(values[0]);
    File ramFile = null;
    try {
      ramFile = File.createTempFile("stackserializationtest", "temp");
      FileOutputStream fos = new FileOutputStream(ramFile);
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(history);
      oos.flush();
      fos.close();
      FileInputStream fis = new FileInputStream(ramFile);
      ObjectInputStream ois = new ObjectInputStream(fis);
      Stack    hist       = (Stack)ois.readObject();
      fis.close();
      Object peekobj = hist.peek(-1);
      String peekstr;
      int expIdx = 0;
      while (peekobj != null) {
        peekstr = peekobj.toString();
        Shell.warning("Unexpected reserializion of Stack entry "+expIdx+" value:"+ peekstr);
        expIdx++;
        peekobj = hist.peek(-1);
      }
    }
    catch (UnsupportedOperationException e) {
      String expected = "java.lang.UnsupportedOperationException: " + Stack.UNSERIALIZABLE_STACK_OBJECT_MESSAGE;
      assertEquals("Stack can not serialize unserializable class", expected, e.toString());
    }
    catch (Exception e) {
      fail("Unexpected Exception during serialization:"+e);
    }
    finally {
      ramFile.delete();
    }
  }

}
class CustomObject {
  Double d;
  Object o;
  String s;

  CustomObject (Double d, Object o, String s) {
    this.d = d;
    this.o = o;
    this.s = s;
  }

  public String toString () {
    return "CustomObject d:"+d+" o:"+o+" s:"+s;
  }
}