package com.laytonsmith.core;
import com.laytonsmith.abstraction.MCPlayer;
import com.laytonsmith.core.compiler.OptimizationUtilities;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.exceptions.CRE.CRECastException;
import com.laytonsmith.core.exceptions.ConfigCompileException;
import com.laytonsmith.core.functions.Exceptions;
import com.laytonsmith.testing.StaticTest;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.Before;
import static com.laytonsmith.testing.StaticTest.SRun;
import static org.mockito.Mockito.*;
/**
*
*/
public class NewExceptionHandlingTest {
@BeforeClass
public static void setUpClass(){
StaticTest.InstallFakeServerFrontend();
}
public String optimize(String script) throws Exception {
return OptimizationUtilities.optimize(script, null);
}
MCPlayer fakePlayer;
@Before
public void setUp() {
fakePlayer = StaticTest.GetOnlinePlayer();
}
@Test public void testBasicKeywordUsage() throws Exception {
assertEquals("complex_try(null,assign(IOException,@e,null),null,assign(Exception,@e,null),null,null)", optimize("try { } catch(IOException @e){ } catch(Exception @e){ } finally { }"));
}
@Test public void testTryFinallyKeywordUsage() throws Exception {
assertEquals("complex_try(msg('a'),msg('b'))", optimize("try { msg(\"a\"); } finally { msg(\"b\"); }"));
}
@Test public void testBasicUsage() throws Exception {
SRun("try { throw(IOException, ''); } catch(IOException @e) { msg('exception'); }", fakePlayer);
verify(fakePlayer).sendMessage("exception");
}
@Test public void testExceptionTrickle() throws Exception {
SRun("try { throw(IOException, ''); } catch(NullPointerException @e) { msg('no run'); } catch(IOException @e){ msg('run'); }", fakePlayer);
verify(fakePlayer).sendMessage("run");
}
@Test public void testExceptionInheritance() throws Exception {
SRun("try { throw(IOException, ''); } catch(Exception @e) { msg('run'); }", fakePlayer);
verify(fakePlayer).sendMessage("run");
}
@Test public void testExceptionObjectCorrect() throws Exception {
SRun(
/* 1 */ "try {\n"
/* 2 */ + "throw(IOException, 'message'); \n"
/* 3 */ + "} catch(IOException @e){ \n"
/* 4 */ + "msg(@e); \n"
/* 5 */ + "}", fakePlayer);
verify(fakePlayer).sendMessage("{causedBy: null, classType: IOException, message: message, stackTrace: {{file: Unknown file, id: <<main code>>, line: 2}}}");
}
@Test public void testExceptionObjectCorrect2() throws Exception {
// Test the line numbers for a complex exception
SRun(
/* 01 */ "proc _a(){\n"
/* 02 */ + "_b();\n"
/* 03 */ + "}\n"
/* 04 */ + "proc _b(){\n"
/* 05 */ + "_c();\n"
/* 06 */ + "}\n"
/* 07 */ + "proc _c(){\n"
/* 08 */ + "throw(IOException, 'message');\n"
/* 09 */ + "}\n"
/* 10 */ + "try {\n"
/* 11 */ + "_a();\n"
/* 12 */ + "} catch(IOException @e){\n"
/* 13 */ + "msg(@e);\n"
/* 14 */ + "}", fakePlayer);
verify(fakePlayer).sendMessage("{causedBy: null, classType: IOException, message: message, stackTrace:"
+ " {"
+ "{file: Unknown file, id: proc _c, line: 8}, "
+ "{file: Unknown file, id: proc _b, line: 5}, "
+ "{file: Unknown file, id: proc _a, line: 2}, "
+ "{file: Unknown file, id: <<main code>>, line: 11}}"
+ "}");
}
@Test public void testExceptionObjectCorrect3() throws Exception {
// Test the line numbers for a complex exception, which is generated internally.
SRun(
/* 01 */ "proc _a(){\n"
/* 02 */ + "_b();\n"
/* 03 */ + "}\n"
/* 04 */ + "proc _b(){\n"
/* 05 */ + "_c();\n"
/* 06 */ + "}\n"
/* 07 */ + "proc _c(){\n"
/* 08 */ + "@a = (1 / dyn(0));\n"
/* 09 */ + "}\n"
/* 10 */ + "try {\n"
/* 11 */ + "_a();\n"
/* 12 */ + "} catch(RangeException @e){\n"
/* 13 */ + "msg(@e);\n"
/* 14 */ + "}", fakePlayer);
verify(fakePlayer).sendMessage("{causedBy: null, classType: RangeException, message: Division by 0!, stackTrace:"
+ " {"
+ "{file: Unknown file, id: proc _c, line: 8}, "
+ "{file: Unknown file, id: proc _b, line: 5}, "
+ "{file: Unknown file, id: proc _a, line: 2}, "
+ "{file: Unknown file, id: <<main code>>, line: 11}}"
+ "}");
}
@Test public void testFinallyRunsOnNormal() throws Exception {
SRun("try { noop(); } catch(Exception @e) { msg('nope'); } finally { msg('run'); }", fakePlayer);
verify(fakePlayer).sendMessage("run");
}
@Test public void testFinallyRunsOnException() throws Exception {
SRun("try { throw(IOException, ''); } catch(Exception @e) { msg('exception'); } finally { msg('run'); }", fakePlayer);
verify(fakePlayer).sendMessage("exception");
verify(fakePlayer).sendMessage("run");
}
@Test public void testFinallyRunsAndReturnIsCorrect() throws Exception {
SRun("proc _a(){ try { noop(); return('value'); } catch(Exception @e) { msg('nope'); } finally { msg('run'); } }"
+ "msg(_a());", fakePlayer);
verify(fakePlayer).sendMessage("run");
verify(fakePlayer).sendMessage("value");
}
@Test public void testHiddenThrowSetsOffLog() throws Exception {
CHLog log = StaticTest.InstallFakeLogger();
StaticTest.SetPrivate(Exceptions.complex_try.class, "doScreamError", true, boolean.class);
try {
SRun("try { throw(IOException, 'hidden'); } finally { throw(CastException, 'shown'); }", fakePlayer);
fail("Expected an exception");
} catch(CRECastException ex){
} catch(Exception ex){
fail("Expected an exception, but of type CRECastException");
}
verify(log).Log(eq(CHLog.Tags.RUNTIME), eq(LogLevel.WARNING), anyString(), any(Target.class));
}
@Test(expected = ConfigCompileException.class)
public void testDuplicateExceptionTypeThrowsException() throws Exception {
SRun("try { } catch(CastException @e){ } catch(CastException @e){ }", fakePlayer);
}
@Test public void testExceptionTypeIsCorrectInMulticatch() throws Exception {
SRun("try { throw(CastException, ''); } catch(CastException @e){ msg('run'); } catch(IOException @e){ msg('no run'); }", fakePlayer);
SRun("try { throw(IOException, ''); } catch(CastException @e){ msg('no run'); } catch(IOException @e){ msg('run'); }", fakePlayer);
verify(fakePlayer, times(2)).sendMessage("run");
}
@Test public void testNestedTryWorks() throws Exception {
SRun("try {"
+ "try {"
+ " throw(IOException, '');"
+ "} catch(CastException @e){"
+ " msg('nope');"
+ "}"
+ "} catch(IOException @ex){"
+ " msg('run');"
+ "}", fakePlayer);
verify(fakePlayer).sendMessage("run");
}
@Test(expected = ConfigCompileException.class)
public void testFinallyMustBeLast() throws Exception {
SRun("try { } finally { } catch(Exception @e){ }", fakePlayer);
}
@Test(expected = ConfigCompileException.class)
public void testFinallyErrors() throws Exception {
SRun("finally { }", fakePlayer);
}
@Test(expected = ConfigCompileException.class)
public void testCatchErrors() throws Exception {
SRun("catch(Exception @e) { }", fakePlayer);
}
@Test(expected = ConfigCompileException.class)
public void testCatchErrors2() throws Exception {
SRun("catch { }", fakePlayer);
}
@Test(expected = ConfigCompileException.class)
public void testCatchOnlyAllows1Parameter1() throws Exception {
SRun("try { } catch(Exception @e, IOException @b) { }", fakePlayer);
}
@Test(expected = ConfigCompileException.class)
public void testCatchOnlyAllows1Parameter2() throws Exception {
SRun("catch(){ }", fakePlayer);
}
@Test(expected = ConfigCompileException.class)
public void testTryAloneFails() throws Exception {
SRun("try{ }", fakePlayer);
}
@Test
public void testCausedBy() throws Exception {
SRun(
/* 01 */ "proc _a(){\n"
/* 02 */ + " throw(IOException, 'original');\n"
/* 03 */ + "}\n"
/* 04 */ + "proc _b(){\n"
/* 05 */ + " _a();\n"
/* 06 */ + "}\n"
/* 07 */ + "try {\n"
/* 08 */ + " try {\n"
/* 09 */ + " _b();\n"
/* 10 */ + " } catch(IOException @e){\n"
/* 11 */ + " throw(CastException, 'new', @e);\n"
/* 12 */ + " }\n"
/* 13 */ + "} catch(CastException @e){\n"
/* 14 */ + " msg(@e);\n"
/* 15 */ + "}\n"
, fakePlayer);
verify(fakePlayer).sendMessage(
"{"
+ "causedBy: {"
+ "causedBy: null, "
+ "classType: IOException, "
+ "message: original, "
+ "stackTrace: {"
+ "{"
+ "file: Unknown file, "
+ "id: proc _a, "
+ "line: 2"
+ "}, {"
+ "file: Unknown file, "
+ "id: proc _b, "
+ "line: 5"
+ "}, {"
+ "file: Unknown file, "
+ "id: <<main code>>, "
+ "line: 9"
+ "}"
+ "}"
+ "}, "
+ "classType: CastException, "
+ "message: new, "
+ "stackTrace: {"
+ "{"
+ "file: Unknown file, "
+ "id: <<main code>>, "
+ "line: 11"
+ "}"
+ "}"
+ "}");
}
@Test(expected = ConfigCompileException.class)
public void testUnknownExceptionType() throws Exception{
SRun("try { } catch(NoTaReAlExCePtIoNtYpE @e){ }", fakePlayer);
}
}