jaroslav@273: /** jaroslav@273: * Back 2 Browser Bytecode Translator jaroslav@273: * Copyright (C) 2012 Jaroslav Tulach jaroslav@273: * jaroslav@273: * This program is free software: you can redistribute it and/or modify jaroslav@273: * it under the terms of the GNU General Public License as published by jaroslav@273: * the Free Software Foundation, version 2 of the License. jaroslav@273: * jaroslav@273: * This program is distributed in the hope that it will be useful, jaroslav@273: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@273: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@273: * GNU General Public License for more details. jaroslav@273: * jaroslav@273: * You should have received a copy of the GNU General Public License jaroslav@273: * along with this program. Look for COPYING file in the top folder. jaroslav@273: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@273: */ jaroslav@273: package org.apidesign.vm4brwsr; jaroslav@273: jaroslav@273: import java.lang.reflect.Method; jaroslav@273: import javax.script.Invocable; jaroslav@273: import javax.script.ScriptEngine; jaroslav@273: import javax.script.ScriptEngineManager; jaroslav@273: import org.testng.Assert; jaroslav@273: import org.testng.ITest; jaroslav@273: import org.testng.annotations.Factory; jaroslav@273: import org.testng.annotations.Test; jaroslav@273: jaroslav@273: /** A TestNG {@link Factory} that seeks for {@link Compare} annotations jaroslav@273: * in provided class and builds set of tests that compare the computations jaroslav@273: * in real as well as JavaScript virtual machines. Use as:
jaroslav@273:  * {@code @}{@link Factory} public static create() {
jaroslav@273:  *   return @{link CompareVMs}.{@link #create(YourClass.class);
jaroslav@273:  * }
jaroslav@273: * jaroslav@273: * @author Jaroslav Tulach jaroslav@273: */ jaroslav@273: public final class CompareVMs implements ITest { jaroslav@273: private final Method m; jaroslav@273: private final boolean js; jaroslav@273: private final CompareVMs first, second; jaroslav@273: private Object value; jaroslav@273: private static Invocable code; jaroslav@273: private static CharSequence codeSeq; jaroslav@273: jaroslav@273: private CompareVMs(Method m, boolean js) { jaroslav@273: this.m = m; jaroslav@273: this.js = js; jaroslav@273: this.first = null; jaroslav@273: this.second = null; jaroslav@273: } jaroslav@273: jaroslav@273: private CompareVMs(Method m, CompareVMs first, CompareVMs second) { jaroslav@273: this.first = first; jaroslav@273: this.second = second; jaroslav@273: this.m = m; jaroslav@273: this.js = false; jaroslav@273: } jaroslav@273: jaroslav@273: private static void compileTheCode(Class clazz) throws Exception { jaroslav@273: if (code != null) { jaroslav@273: return; jaroslav@273: } jaroslav@273: StringBuilder sb = new StringBuilder(); jaroslav@273: class SkipMe extends GenJS { jaroslav@273: public SkipMe(Appendable out) { jaroslav@273: super(out); jaroslav@273: } jaroslav@273: jaroslav@273: @Override jaroslav@273: protected boolean requireReference(String cn) { jaroslav@273: if (cn.contains("CompareVMs")) { jaroslav@273: return true; jaroslav@273: } jaroslav@273: return super.requireReference(cn); jaroslav@273: } jaroslav@273: jaroslav@273: jaroslav@273: } jaroslav@273: SkipMe sm = new SkipMe(sb); jaroslav@273: sm.doCompile(CompareVMs.class.getClassLoader(), StringArray.asList( jaroslav@273: clazz.getName().replace('.', '/') jaroslav@273: )); jaroslav@273: jaroslav@273: ScriptEngineManager sem = new ScriptEngineManager(); jaroslav@273: ScriptEngine js = sem.getEngineByExtension("js"); jaroslav@273: jaroslav@273: Object res = js.eval(sb.toString()); jaroslav@273: Assert.assertTrue(js instanceof Invocable, "It is invocable object: " + res); jaroslav@273: code = (Invocable)js; jaroslav@273: codeSeq = sb; jaroslav@273: } jaroslav@273: jaroslav@273: jaroslav@273: public static Object[] create(Class clazz) { jaroslav@273: Method[] arr = clazz.getMethods(); jaroslav@273: Object[] ret = new Object[3 * arr.length]; jaroslav@273: int cnt = 0; jaroslav@273: for (Method m : arr) { jaroslav@273: Compare c = m.getAnnotation(Compare.class); jaroslav@273: if (c == null) { jaroslav@273: continue; jaroslav@273: } jaroslav@273: final CompareVMs real = new CompareVMs(m, false); jaroslav@273: final CompareVMs js = new CompareVMs(m, true); jaroslav@273: ret[cnt++] = real; jaroslav@273: ret[cnt++] = js; jaroslav@273: ret[cnt++] = new CompareVMs(m, real, js); jaroslav@273: } jaroslav@273: Object[] r = new Object[cnt]; jaroslav@273: for (int i = 0; i < cnt; i++) { jaroslav@273: r[i] = ret[i]; jaroslav@273: } jaroslav@273: return r; jaroslav@273: } jaroslav@273: jaroslav@273: @Test public void runTest() throws Throwable { jaroslav@273: if (first != null) { jaroslav@273: Object v1 = first.value; jaroslav@273: Object v2 = second.value; jaroslav@273: if (v1 instanceof Number) { jaroslav@273: v1 = ((Number)v1).doubleValue(); jaroslav@273: } jaroslav@273: Assert.assertEquals(v1, v2, "Comparing results"); jaroslav@273: } else { jaroslav@273: if (js) { jaroslav@273: try { jaroslav@273: compileTheCode(m.getDeclaringClass()); jaroslav@273: Object inst = code.invokeFunction(m.getDeclaringClass().getName().replace('.', '_'), false); jaroslav@273: value = code.invokeMethod(inst, m.getName() + "__I"); jaroslav@273: } catch (Exception ex) { jaroslav@273: throw new AssertionError(StaticMethodTest.dumpJS(codeSeq)).initCause(ex); jaroslav@273: } jaroslav@273: } else { jaroslav@273: value = m.invoke(m.getDeclaringClass().newInstance()); jaroslav@273: } jaroslav@273: } jaroslav@273: } jaroslav@273: jaroslav@273: @Override jaroslav@273: public String getTestName() { jaroslav@273: if (first != null) { jaroslav@273: return m.getName() + "[Compare]"; jaroslav@273: } jaroslav@273: return m.getName() + (js ? "[JavaScript]" : "[Java]"); jaroslav@273: } jaroslav@273: }