/* * Copyright 2008 CoreMedia AG * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package net.jangaroo.test.integration; import net.jangaroo.jooc.Jooc; import net.jangaroo.test.JooTestCase; import org.mozilla.javascript.*; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; /** * A JooTestCase to be executed at runtime * <p/> * This class adds a global evaluation context and methods to load and execute script code. * * @author Andreas Gawecki */ public abstract class JooRuntimeTestCase extends JooTestCase { protected Global global; private Context cx; private static final String CLASS_JS_FILE_PATH = "joo/jangaroo-runtime.module.js"; // Jooc. + /* "-debug" + */Jooc.OUTPUT_FILE_SUFFIX; public static String jsFileName(final String qualifiedJooClassName) { return qualifiedJooClassName.replace('.', File.separatorChar) + Jooc.OUTPUT_FILE_SUFFIX; } public static String asFileName(final String qualifiedJooClassName) { return qualifiedJooClassName.replace('.', File.separatorChar) + Jooc.AS_SUFFIX; } public JooRuntimeTestCase(String name) { super(name); } static public class Global extends ScriptableObject { private final Joo joo = new Joo(this); public Joo getJoo() { return joo; } public String getClassName() { return "global"; } public Object eval(Context cx, String script) throws Exception { //System.out.print("evaluating script '" + script + "': "); Reader reader = new StringReader(script); Object result = cx.evaluateReader(this, reader, "", 1, null); return result; } public static void setTimeout(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws Exception { Function fn = (Function) args[0]; Object n = args[1]; // no invoke later yet... fn.call(cx, thisObj, fn, new Object[]{}); } public void printJsResult(Object result) throws Exception { System.out.println(Context.toString(result) + " (" + result.getClass().getName() + ")"); } } static public class Joo extends ScriptableObject { private Global global; private String jsDir; public Joo(Global global) { this.global = global; } public String getClassName() { return "joo"; } public String getJsDir() { return jsDir; } public void setJsDir(final String jsDir) { this.jsDir = jsDir; } public static void trace(Context cx, Scriptable thisObj, Object[] args, Function funObj) { for (int i = 0; i < args.length; i++) { if (i > 0) System.out.print(" "); // Convert the arbitrary JavaScript value into a string form. String s = Context.toString(args[i]); System.out.print(s); } System.out.println(); } public static Scriptable loadScriptAsync(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws Exception { _loadScript(cx, thisObj, args, funObj); // TODO: make it really async? return new HTMLScriptElement((String)args[0]); } private static class HTMLScriptElement extends ScriptableObject { private HTMLScriptElement(String src) { defineProperty("src", src, ScriptableObject.EMPTY); } @Override public String getClassName() { return "HTMLScriptElement"; } } public static void _loadScript(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws Exception { Joo joo = (Joo) thisObj; for (Object arg : args) { // Convert the arbitrary JavaScript value into a string form. String s = Context.toString(arg); joo.load(cx, s); } } public Object load(Context cx, File jsFile) throws Exception { String jsFileName = jsFile.getAbsolutePath(); System.out.println("loading script " + jsFileName); Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(jsFile), "UTF-8")); return cx.evaluateReader(global, reader, jsFileName, 1, null); } public void load(Context cx, String jsFileName) throws Exception { load(cx, new File(getJsDir(), jsFileName)); } public void import_(Context cx, String qualifiedJooClassName) throws Exception { global.eval(cx, "joo.classLoader.import_('" + qualifiedJooClassName + "')"); } } protected String readClassCode(String qualifiedJooClassName) throws Exception { String jsFileName = jsFileName(qualifiedJooClassName); File file = new File(global.joo.getJsDir(), jsFileName); Reader in = new InputStreamReader(new FileInputStream(file), "UTF-8"); StringBuilder builder = new StringBuilder(); int n; char[] buf = new char[512]; while ((n = in.read()) > 0) { builder.append(buf, 0, n); } return builder.toString(); } protected void setUp() throws Exception { super.setUp(); global = new Global(); cx = ContextFactory.getGlobal().enterContext(); cx.setLanguageVersion(Context.VERSION_1_5); cx.initStandardObjects(global); global.defineFunctionProperties(new String[]{"setTimeout"}, Global.class, ScriptableObject.EMPTY); global.defineProperty("window", global, ScriptableObject.EMPTY); global.defineProperty("joo", Global.class, ScriptableObject.EMPTY); // add a property to Array's prototype to assure safe rewriting of Array for [each] loops: ((ScriptableObject)((ScriptableObject)global.get("Array")).get("prototype")).defineProperty("unsafeArrayEnumeration", "UNSAFE ARRAY ENUMERATION!", ScriptableObject.EMPTY); global.joo.defineProperty("debug", true, ScriptableObject.EMPTY); global.joo.defineProperty("baseUrl", "", ScriptableObject.EMPTY); global.joo.defineFunctionProperties(new String[]{"trace", "_loadScript", "loadScriptAsync"}, Joo.class, ScriptableObject.EMPTY); global.joo.setJsDir(destinationDir); load(CLASS_JS_FILE_PATH); } protected void tearDown() throws Exception { super.tearDown(); Context.exit(); } protected void load(String jsFileName) throws Exception { global.joo.load(cx, jsFileName); } protected void printJsResult(Object result) throws Exception { global.printJsResult(result); } protected Object load(File jsFile) throws Exception { return global.joo.load(cx, jsFile); } protected Object loadAll() throws Exception { return loadAll(new File(global.joo.getJsDir())); } protected Object loadAll(final File dir) throws Exception { File[] files = dir.listFiles(); Object lastResult = null; for (File file :files) { if (file.isDirectory()) { lastResult = loadAll(file); } else if (file.getName().endsWith(".js")) { lastResult = load(file); } } return lastResult; } protected void import_(String qualifiedJooClassName) throws Exception { global.joo.import_(cx, qualifiedJooClassName); } protected void initClass(String qualifiedJooClassName) throws Exception { eval(Jooc.CLASS_LOADER_FULLY_QUALIFIED_NAME + ".init(" + qualifiedJooClassName + ")"); } protected void complete() throws Exception { eval(Jooc.CLASS_LOADER_FULLY_QUALIFIED_NAME + ".complete();"); } protected void runClass(String qualifiedJooClassName) throws Exception { import_(qualifiedJooClassName); complete(); //initClass(qualifiedJooClassName); eval(qualifiedJooClassName + ".main()"); } protected Object eval(String script) throws Exception { return global.eval(cx, script); } protected void expectString(String expected, String script) throws Exception { Object result = eval(script); String actual = null; if (result instanceof String) actual = (String) result; else { if (result == null) { fail("expected string result, found null"); } else { fail("expected string result, found: " + result.getClass().getName()); } } if (!expected.equals(actual)) { if (actual.length() == expected.length()) { int i = 0; while (actual.charAt(i) == expected.charAt(i)) i++; fail("expected: \"" + expected + "\", found: \"" + actual + "\"\nstrings differ at index " + i + ""); } else fail("expected length " + expected.length() + ": \"" + expected + "\", found length " + actual.length() + ": \"" + actual + "\""); } } protected void expectSubstring(String expected, String script) throws Exception { Object result = eval(script); String actual = null; if (result instanceof String) actual = (String) result; else fail("expected string result, found: " + result.getClass().getName()); if (!actual.contains(expected)) { fail("expected substring '" + expected + "' not found within: " + unparse(result)); } } protected void expectNumber(double expected, String script) throws Exception { Object result = eval(script); double actual = 0; if (result instanceof Number) actual = ((Number) result).doubleValue(); else fail("expected numeric result, found: " + result.getClass().getName() + ": " + unparse(result)); assertEquals(expected, actual, 0.00000000001); } protected void expectBoolean(boolean expected, String script) throws Exception { Object result = eval(script); boolean actual = false; if (result instanceof Boolean) actual = (Boolean) result; else fail("expected boolean result, found: " + result.getClass().getName()); assertEquals(expected, actual); } protected void expectException(String script) throws Exception { try { Object result = eval(script); fail("expected exception, but got regular result: " + unparse(result)); } catch (Exception e) { // ok } } private String unparse(final Object result) { if (result instanceof String) { return "'" + result + "'"; } return result.toString(); } public void testRhino() throws Exception { expectNumber(23, "23"); expectNumber(23.1, "22.1+1"); } }