package jhydra.core.scripting;
import jhydra.core.config.IRuntimeConfig;
import jhydra.core.exceptions.FatalException;
import jhydra.core.exceptions.RecoverableException;
import jhydra.core.logging.ILog;
import jhydra.core.scripting.exceptions.ClassNotInScriptFileException;
import jhydra.core.scripting.exceptions.ScriptExecutionException;
import jhydra.core.scripting.exceptions.ScriptNavigationException;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Author: jantic
* Date: 3/29/13
*/
public class RobustScriptTests {
/***Tests normally executing script ****************************************/
@Test
public void normallyExecutingSingleInnerScript_execute_runElapsedTime_LessThanSecond() throws FatalException, RecoverableException{
final RobustScript script = getNormallyExecutingSingleInnerScript();
final Boolean expected = true;
final DateTime startTime = DateTime.now();
script.execute();
final DateTime endTime = DateTime.now();
final Interval interval = new Interval(startTime, endTime);
final Long duration = interval.toDurationMillis();
final Boolean actual = duration < 1000;
Assert.assertEquals(expected, actual);
}
/***Tests abnormally (but recoverable) exiting script ****************************************/
@Test(expected = ScriptExecutionException.class)
public void recoverableAbnormallyExitingSingleInnerScript_execute_throwsRecoverableException() throws FatalException, RecoverableException{
final RobustScript script = getRecoverableAbnormallyExitingSingleInnerScript();
script.execute();
}
//Note: Should NOT retry, because it's a single script with no parent script and therefore its parent
//test case would just be retried instead.
@Test
public void recoverableAbnormallyExitingSingleInnerScript_execute_runElapsedTime_LessThanOneSecond() throws FatalException, RecoverableException{
final RobustScript script = getRecoverableAbnormallyExitingSingleInnerScript();
final Boolean expected = true;
final DateTime startTime = DateTime.now();
try{
script.execute();
}
catch(RecoverableException e){
//ignore for testing purposes.
}
final DateTime endTime = DateTime.now();
final Interval interval = new Interval(startTime, endTime);
final Long duration = interval.toDurationMillis();
//1 attempt, no pause
final Boolean actual = duration < 1000;
Assert.assertEquals(expected, actual);
}
//NOTE: We want only the most immediate error throwing script to retry. We don't want
//all the calling scripts to retry as well. This tests that behavior
@Test
public void recoverableAbnormallyExitingMultipleInnerScripts_execute_runElapsedTime_OverTwoSeconds() throws FatalException, RecoverableException{
final RobustScript script = getRecoverableAbnormallyExitingMultipleInnerScripts();
final Boolean expected = true;
final DateTime startTime = DateTime.now();
try{
script.execute();
}
catch(RecoverableException e){
//ignore for testing purposes.
}
final DateTime endTime = DateTime.now();
final Interval interval = new Interval(startTime, endTime);
final Long duration = interval.toDurationMillis();
//3 attempts expected, 2 of which are followed by 1 second pauses.
final Boolean actual = duration >= 2000 && duration < 3000;
Assert.assertEquals(expected, actual);
}
/***Tests abnormally (non-recoverable) exiting script ****************************************/
//Note: Should NOT retry!
@Test(expected = ClassNotInScriptFileException.class)
public void nonRecoverableAbnormallyExitingSingleInnerScript_execute_throwsInnerScriptsException() throws FatalException, RecoverableException{
final RobustScript script = getNonRecoverableAbnormallyExitingSingleInnerScript();
script.execute();
}
@Test
public void nonRecoverableAbnormallyExitingSingleInnerScript_execute_runElapsedTime_LessThanSecond() throws FatalException, RecoverableException{
final RobustScript script = getNonRecoverableAbnormallyExitingSingleInnerScript();
final Boolean expected = true;
final DateTime startTime = DateTime.now();
try{
script.execute();
}
catch(FatalException e){
//just ignore for testing purposes.
}
final DateTime endTime = DateTime.now();
final Interval interval = new Interval(startTime, endTime);
final Long duration = interval.toDurationMillis();
final Boolean actual = duration < 1000;
Assert.assertEquals(expected, actual);
}
//Should NOT retry!
@Test
public void nonRecoverableAbnormallyExitingMultipleInnerScripts_execute_runElapsedTime_LessThanSecond() throws FatalException, RecoverableException{
final RobustScript script = getNonRecoverableAbnormallyExitingMultipleInnerScripts();
final Boolean expected = true;
final DateTime startTime = DateTime.now();
try{
script.execute();
}
catch(FatalException e){
//ignore for testing purposes.
}
final DateTime endTime = DateTime.now();
final Interval interval = new Interval(startTime, endTime);
final Long duration = interval.toDurationMillis();
//3 attempts expected, 2 of which are followed by 1 second pauses.
final Boolean actual = duration < 1000;
Assert.assertEquals(expected, actual);
}
/*****************************************************************************************/
/***PRIVATE METHODS***********************************************************************/
/*****************************************************************************************/
private RobustScript getNormallyExecutingSingleInnerScript(){
final IRuntimeConfig config = getNormalRuntimeConfig();
final ILog log = mock(ILog.class);
final RobustScript innerScript = getNormallyExecutingScript();
return new RobustScript(innerScript, config, log);
}
private IRuntimeConfig getNormalRuntimeConfig(){
final IRuntimeConfig config = mock(IRuntimeConfig.class);
when(config.getScriptMaxNumTries()).thenReturn(3);
when(config.getScriptWaitSecondsBetweenAttempts()).thenReturn(1);
when(config.getScriptTimeoutSeconds()).thenReturn(2);
return config;
}
private RobustScript getNormallyExecutingScript(){
return mock(RobustScript.class);
}
private RobustScript getRecoverableAbnormallyExitingSingleInnerScript() throws RecoverableException, FatalException {
final IRuntimeConfig config = getNormalRuntimeConfig();
final ILog log = mock(ILog.class);
final RobustScript innerScript = getRecoverableAbnormallyExitingScript();
return new RobustScript(innerScript, config, log);
}
private RobustScript getRecoverableAbnormallyExitingMultipleInnerScripts() throws RecoverableException, FatalException {
final IRuntimeConfig config = getNormalRuntimeConfig();
final ILog log = mock(ILog.class);
final RobustScript innerScript = getRecoverableAbnormallyExitingScript();
final RobustScript middleScript1 = new RobustScript(innerScript, config, log);
final RobustScript middleScript2 = new RobustScript(middleScript1, config, log);
return new RobustScript(middleScript2, config, log);
}
private RobustScript getRecoverableAbnormallyExitingScript() throws RecoverableException, FatalException {
final RobustScript script = mock(RobustScript.class);
Mockito.doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) throws ScriptNavigationException {
recoverableAbnormalExitScriptExecute();
return null;
}
}).when(script).execute();
return script;
}
private void recoverableAbnormalExitScriptExecute() throws ScriptNavigationException {
throw new ScriptNavigationException("These are the failure details.");
}
private RobustScript getNonRecoverableAbnormallyExitingMultipleInnerScripts() throws RecoverableException, FatalException {
final IRuntimeConfig config = getNormalRuntimeConfig();
final ILog log = mock(ILog.class);
final RobustScript innerScript = getNonRecoverableAbnormallyExitingScript();
final RobustScript middleScript1 = new RobustScript(innerScript, config, log);
final RobustScript middleScript2 = new RobustScript(middleScript1, config, log);
return new RobustScript(middleScript2, config, log);
}
private RobustScript getNonRecoverableAbnormallyExitingSingleInnerScript() throws RecoverableException, FatalException {
final IRuntimeConfig config = getNormalRuntimeConfig();
final ILog log = mock(ILog.class);
final RobustScript innerScript = getNonRecoverableAbnormallyExitingScript();
return new RobustScript(innerScript, config, log);
}
private RobustScript getNonRecoverableAbnormallyExitingScript() throws RecoverableException, FatalException {
final RobustScript script = mock(RobustScript.class);
Mockito.doAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) throws ClassNotInScriptFileException {
nonRecoverableAbnormalExitScriptExecute();
return null;
}
}).when(script).execute();
return script;
}
private void nonRecoverableAbnormalExitScriptExecute() throws ClassNotInScriptFileException {
throw new ClassNotInScriptFileException("These are the failure details.", new Exception());
}
}