2 * Back 2 Browser Bytecode Translator
3 * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 2 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. Look for COPYING file in the top folder.
16 * If not, see http://opensource.org/licenses/GPL-2.0.
18 package org.apidesign.bck2brwsr.vmtest;
21 import java.io.FileWriter;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.lang.reflect.Method;
26 import java.util.Enumeration;
28 import java.util.WeakHashMap;
29 import javax.script.Invocable;
30 import javax.script.ScriptEngine;
31 import javax.script.ScriptEngineManager;
32 import org.apidesign.vm4brwsr.Bck2Brwsr;
33 import org.testng.Assert;
34 import org.testng.ITest;
35 import org.testng.annotations.Factory;
36 import org.testng.annotations.Test;
38 /** A TestNG {@link Factory} that seeks for {@link Compare} annotations
39 * in provided class and builds set of tests that compare the computations
40 * in real as well as JavaScript virtual machines. Use as:<pre>
41 * {@code @}{@link Factory} public static create() {
42 * return @{link VMTest}.{@link #create(YourClass.class);
45 * @author Jaroslav Tulach <jtulach@netbeans.org>
47 public final class VMTest implements ITest {
48 private final Run first, second;
49 private final Method m;
51 private VMTest(Method m, Run first, Run second) {
57 /** Inspects <code>clazz</code> and for each {@lik Compare} method creates
58 * instances of tests. Each insteance runs the test in different virtual
59 * machine and at the end they compare the results.
61 * @param clazz the class to inspect
62 * @return the set of created tests
64 public static Object[] create(Class<?> clazz) {
65 Method[] arr = clazz.getMethods();
66 Object[] ret = new Object[3 * arr.length];
68 for (Method m : arr) {
69 Compare c = m.getAnnotation(Compare.class);
73 final Run real = new Run(m, false);
74 final Run js = new Run(m, true);
77 ret[cnt++] = new VMTest(m, real, js);
79 Object[] r = new Object[cnt];
80 for (int i = 0; i < cnt; i++) {
86 /** Test that compares the previous results.
89 @Test(dependsOnGroups = "run") public void compareResults() throws Throwable {
90 Object v1 = first.value;
91 Object v2 = second.value;
92 if (v1 instanceof Number) {
93 v1 = ((Number)v1).doubleValue();
95 Assert.assertEquals(v2, v1, "Comparing results");
99 * @return name of the tested method followed by a suffix
102 public String getTestName() {
103 return m.getName() + "[Compare]";
106 /** Helper method that inspects the classpath and loads given resource
107 * (usually a class file). Used while running tests in Rhino.
109 * @param name resource name to find
110 * @return the array of bytes in the given resource
111 * @throws IOException I/O in case something goes wrong
113 public static byte[] read(String name) throws IOException {
115 Enumeration<URL> en = VMTest.class.getClassLoader().getResources(name);
116 while (en.hasMoreElements()) {
117 u = en.nextElement();
120 throw new IOException("Can't find " + name);
122 try (InputStream is = u.openStream()) {
124 arr = new byte[is.available()];
126 while (offset < arr.length) {
127 int len = is.read(arr, offset, arr.length - offset);
129 throw new IOException("Can't read " + name);
137 public static final class Run implements ITest {
138 private final Method m;
139 private final boolean js;
141 private Invocable code;
142 private CharSequence codeSeq;
143 private static final Map<Class,Object[]> compiled = new WeakHashMap<Class,Object[]>();
145 private Run(Method m, boolean js) {
150 private void compileTheCode(Class<?> clazz) throws Exception {
151 final Object[] data = compiled.get(clazz);
153 code = (Invocable) data[0];
154 codeSeq = (CharSequence) data[1];
157 StringBuilder sb = new StringBuilder();
158 Bck2Brwsr.generate(sb, VMTest.class.getClassLoader());
160 ScriptEngineManager sem = new ScriptEngineManager();
161 ScriptEngine js = sem.getEngineByExtension("js");
163 sb.append("\nfunction initVM() {"
164 + "\n return bck2brwsr("
165 + "\n function(name) { return org.apidesign.bck2brwsr.vmtest.VMTest.read(name);}"
169 Object res = js.eval(sb.toString());
170 Assert.assertTrue(js instanceof Invocable, "It is invocable object: " + res);
171 code = (Invocable) js;
173 compiled.put(clazz, new Object[] { code, codeSeq });
176 @Test(groups = "run") public void executeCode() throws Throwable {
179 compileTheCode(m.getDeclaringClass());
180 Object vm = code.invokeFunction("initVM");
181 Object inst = code.invokeMethod(vm, "loadClass", m.getDeclaringClass().getName());
182 value = code.invokeMethod(inst, m.getName() + "__" + computeSignature(m));
183 } catch (Exception ex) {
184 throw new AssertionError(dumpJS(codeSeq)).initCause(ex);
187 value = m.invoke(m.getDeclaringClass().newInstance());
191 public String getTestName() {
192 return m.getName() + (js ? "[JavaScript]" : "[Java]");
195 private static String computeSignature(Method m) {
196 StringBuilder sb = new StringBuilder();
197 appendType(sb, m.getReturnType());
198 for (Class<?> c : m.getParameterTypes()) {
201 return sb.toString();
204 private static void appendType(StringBuilder sb, Class<?> t) {
209 if (t.isPrimitive()) {
211 if (t == int.class) {
214 if (t == short.class) {
217 if (t == byte.class) {
220 if (t == boolean.class) {
223 if (t == long.class) {
226 if (t == float.class) {
229 if (t == double.class) {
232 assert ch != -1 : "Unknown primitive type " + t;
238 appendType(sb, t.getComponentType());
242 sb.append(t.getName().replace('.', '_'));
247 static StringBuilder dumpJS(CharSequence sb) throws IOException {
248 File f = File.createTempFile("execution", ".js");
249 FileWriter w = new FileWriter(f);
252 return new StringBuilder(f.getPath());