/*
* Copyright 2007-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package org.visage.tools.script;
import org.visage.api.VisageScriptEngine;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.List;
import javax.script.*;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaFileObject;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Tests for Visage scripting engine.
*
* @author Tom Ball
*/
public class VisageScriptEngineTest {
private VisageScriptEngine engine;
private ByteArrayOutputStream out;
private PrintStream stdout;
private static PrintStream originalOut;
private ByteArrayOutputStream err;
private PrintStream stderr;
private static PrintStream originalErr;
@BeforeClass
public static void saveSystemOutErr() {
originalOut = System.out;
originalErr = System.err;
}
@Before
public void setUp() {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine scrEng = manager.getEngineByName("visage");
assertTrue(scrEng instanceof VisageScriptEngine);
engine = (VisageScriptEngine)scrEng;
out = new ByteArrayOutputStream();
stdout = new PrintStream(out);
System.setOut(stdout);
err = new ByteArrayOutputStream();
stderr = new PrintStream(err);
System.setErr(stderr);
}
@Test
public void getEngineByName() throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine scrEng = manager.getEngineByName("visage");
assertTrue(scrEng instanceof VisageScriptEngine);
}
@Test
public void getEngineByBadName() throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine scrEng = manager.getEngineByName("java");
assertFalse(scrEng instanceof VisageScriptEngine);
}
@Test
public void getEngineByExtension() throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine scrEng = manager.getEngineByName("visage");
assertTrue(scrEng instanceof VisageScriptEngine);
}
@Test
public void getEngineByBadExtension() throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine scrEng = manager.getEngineByName("java");
assertFalse(scrEng instanceof VisageScriptEngine);
}
@Test
public void simpleScript() throws Exception {
engine.eval("java.lang.System.out.print(\"Hello, world\");" +
"java.lang.System.out.flush();");
assertEquals("Hello, world", getOutput());
}
@Test
public void scriptWithBinding() throws Exception {
Bindings bindings = new SimpleBindings();
bindings.put("who", "world");
engine.eval("java.lang.System.out.print(\"Hello, {who}\");" +
"java.lang.System.out.flush();", bindings);
assertEquals("Hello, world", getOutput());
}
@Test
public void scriptWithConflictingBinding() throws Exception {
Bindings bindings = new SimpleBindings();
bindings.put("who", "world");
bindings.put("howMany", "lots"); // type of howMany attribute is String
engine.eval("var howMany: Integer = 1;" + // versus declared howMany's Integer
"java.lang.System.out.print(\"Hello, {who}\");" +
"java.lang.System.out.flush();", bindings);
assertEquals("Hello, world", getOutput());
}
@Test
public void scriptResult() throws Exception {
Object ret = engine.eval("var a = 1; a + 2;");
assertNotNull(ret);
assertEquals(((Number)ret).intValue(), 3);
}
@Test
public void scriptSequenceVarResult() throws Exception {
Object ret = engine.eval("var elements:String[] = [ 'abc' ]; elements[0]");
assertNotNull(ret);
assertEquals(ret, "abc");
assertEquals(engine.get("elements").toString(), "[ abc ]");
}
@Test
public void compiledScript() throws Exception {
engine.put("who", "world");
CompiledScript script = engine.compile(
"java.lang.System.out.print(\"Hello, {who}\");" +
"java.lang.System.out.flush();");
script.eval();
assertEquals("Hello, world", getOutput());
}
@Test
public void verifyGlobalBindings() throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
manager.put("greeting", "Hello");
engine = (VisageScriptEngine)manager.getEngineByExtension("visage");
String script = "java.lang.System.out.print(\"{greeting}, {who}\");" +
"java.lang.System.out.flush();";
Bindings bindings = new SimpleBindings();
bindings.put("who", "world");
engine.eval(script, bindings);
assertEquals("Hello, world", getOutput());
bindings.clear();
bindings.put("who", "moon");
engine.eval(script, bindings);
assertEquals("Hello, moon", getOutput());
}
@Test
public void invokeFunction() throws Exception {
engine.compile(
"function add(a:Integer, b:Integer):Integer { return a + b; }");
Object ret = ((Invocable)engine).invokeFunction("add", 1, 2);
assertNotNull(ret);
assertEquals(((Number)ret).intValue(), 3);
}
@Test
public void invokeMethod() throws Exception {
String script =
"class Test{ function hello():String {return \"Hello, world\";}}" +
"function create():Test { return new Test(); }";
engine.compile(script);
Object test = engine.invokeFunction("create");
assertNotNull(test);
Object ret = engine.invokeMethod(test, "hello");
assertNotNull(ret);
assertEquals("Hello, world", ret.toString());
}
@Test
public void invokeMethodParam() throws Exception {
String script =
"class Test{ function getClass(o: Test): java.lang.Class {return o.getClass();}}" +
"function create():Test { return new Test(); }";
engine.compile(script);
Object test = engine.invokeFunction("create");
assertNotNull(test);
Object ret = engine.invokeMethod(test, "getClass", test);
assertNotNull(ret);
assertEquals( test.getClass(), ret );
}
@Test
public void verifyErrorLineNumber() throws Exception {
// test that bindings don't disturb a script's line numbers when an error is reported
String script =
"class Test { // 1\n" +
" function hello():String { // 2\n" +
" return \"Hello, world\"; // 3\n" +
" } // 4\n" +
"} // 5\n" +
" // 6\n" +
"var t = new Test(); // 7\n" +
"t.hello(1); // invalid parameter 8\n";
DiagnosticCollector<JavaFileObject> diags = new DiagnosticCollector<JavaFileObject>();
VisageScriptEngine visageEngine = (VisageScriptEngine)engine;
try {
visageEngine.eval(script, diags);
fail("script should have thrown ScriptException due to bad code");
} catch (ScriptException e) {
List<Diagnostic<? extends JavaFileObject>> errorList = diags.getDiagnostics();
assertTrue(errorList.size() == 1);
Diagnostic<? extends JavaFileObject> error = errorList.get(0);
assertTrue(error.getKind() == Diagnostic.Kind.ERROR);
assertEquals(8, error.getLineNumber());
}
}
@Test
public void verifyErrorLineNumberWithBinding() throws Exception {
// test that bindings don't disturb a script's line numbers when an error is reported
String script =
"class Test { // 1\n" +
" function hello(s:String):String { // 2\n" +
" return \"hello, {s}\"; // 3\n" +
" } // 4\n" +
"} // 5\n" +
"var hello = who; // who defined in binding 6\n" +
"var t = new Test(); // 7\n" +
"t.hello(1); // invalid parameter 8\n";
Bindings bindings = new SimpleBindings();
bindings.put("who", "world");
DiagnosticCollector<JavaFileObject> diags = new DiagnosticCollector<JavaFileObject>();
VisageScriptEngine visageEngine = (VisageScriptEngine)engine;
try {
visageEngine.eval(script, bindings, diags);
fail("script should have thrown ScriptException due to bad code");
} catch (ScriptException e) {
List<Diagnostic<? extends JavaFileObject>> errorList = diags.getDiagnostics();
assertTrue(errorList.size() == 1);
Diagnostic<? extends JavaFileObject> error = errorList.get(0);
assertTrue(error.getKind() == Diagnostic.Kind.ERROR);
assertEquals(8, error.getLineNumber());
}
}
@Test
public void verifyNoExtraBindings() throws Exception {
// When entering var declarations in VisagePad, a user may reference
// a variable before declaring it. This should return an error, but
// the script engine used to declare the var a binding, causing the
// declaration to fail when the var's type is later declared.
// This test verifies that no bindings are inferred during an eval.
try {
engine.eval("x = 2;");
fail("script should have failed due to missing declaration");
} catch (ScriptException e) {
Object result = engine.eval("var x = 2;");
assertEquals(2, ((Number)result).intValue()); // var declarations now return results
}
}
private String getOutput() {
stdout.flush();
String output = out.toString();
out.reset();
return output;
}
@AfterClass
public static void restoreSystemOutErr() {
System.setOut(originalOut);
System.setErr(originalErr);
}
}