vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Wed, 12 Dec 2012 09:09:42 +0100
changeset 306 f36b3c273de6
parent 297 a20721a10717
parent 303 c12342170235
permissions -rw-r--r--
Merging lazyvm into default branch - it seems to be stable enough to be used.
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     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.
     8  *
     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.
    13  *
    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.
    17  */
    18 package org.apidesign.vm4brwsr;
    19 
    20 import java.lang.reflect.Method;
    21 import java.util.Map;
    22 import java.util.WeakHashMap;
    23 import javax.script.Invocable;
    24 import javax.script.ScriptContext;
    25 import javax.script.ScriptEngine;
    26 import javax.script.ScriptEngineManager;
    27 import org.testng.Assert;
    28 import org.testng.ITest;
    29 import org.testng.annotations.Factory;
    30 import org.testng.annotations.Test;
    31 
    32 /** A TestNG {@link Factory} that seeks for {@link Compare} annotations
    33  * in provided class and builds set of tests that compare the computations
    34  * in real as well as JavaScript virtual machines. Use as:<pre>
    35  * {@code @}{@link Factory} public static create() {
    36  *   return @{link CompareVMs}.{@link #create(YourClass.class);
    37  * }</pre>
    38  *
    39  * @author Jaroslav Tulach <jtulach@netbeans.org>
    40  */
    41 public final class CompareVMs implements ITest {
    42     private final Run first, second;
    43     private final Method m;
    44     
    45     private CompareVMs(Method m, Run first, Run second) {
    46         this.first = first;
    47         this.second = second;
    48         this.m = m;
    49     }
    50 
    51     public static Object[] create(Class<?> clazz) {
    52         Method[] arr = clazz.getMethods();
    53         Object[] ret = new Object[3 * arr.length];
    54         int cnt = 0;
    55         for (Method m : arr) {
    56             Compare c = m.getAnnotation(Compare.class);
    57             if (c == null) {
    58                 continue;
    59             }
    60             final Run real = new Run(m, false);
    61             final Run js = new Run(m, true);
    62             ret[cnt++] = real;
    63             ret[cnt++] = js;
    64             ret[cnt++] = new CompareVMs(m, real, js);
    65         }
    66         Object[] r = new Object[cnt];
    67         for (int i = 0; i < cnt; i++) {
    68             r[i] = ret[i];
    69         }
    70         return r;
    71     }
    72     
    73     @Test(dependsOnGroups = "run") public void compareResults() throws Throwable {
    74         Object v1 = first.value;
    75         Object v2 = second.value;
    76         if (v1 instanceof Number) {
    77             v1 = ((Number)v1).doubleValue();
    78         }
    79         Assert.assertEquals(v2, v1, "Comparing results");
    80     }
    81     
    82     @Override
    83     public String getTestName() {
    84         return m.getName() + "[Compare]";
    85     }
    86     
    87     public static final class Run implements ITest {
    88         private final Method m;
    89         private final boolean js;
    90         Object value;
    91         private Invocable code;
    92         private CharSequence codeSeq;
    93         private static final Map<Class,Object[]> compiled = new WeakHashMap<Class,Object[]>();
    94 
    95         private Run(Method m, boolean js) {
    96             this.m = m;
    97             this.js = js;
    98         }
    99 
   100         private void compileTheCode(Class<?> clazz) throws Exception {
   101             final Object[] data = compiled.get(clazz);
   102             if (data != null) {
   103                 code = (Invocable) data[0];
   104                 codeSeq = (CharSequence) data[1];
   105                 return;
   106             }
   107             StringBuilder sb = new StringBuilder();
   108             Bck2Brwsr.generate(sb, CompareVMs.class.getClassLoader());
   109 
   110             ScriptEngineManager sem = new ScriptEngineManager();
   111             ScriptEngine js = sem.getEngineByExtension("js");
   112             js.getContext().setAttribute("loader", new BytesLoader(), ScriptContext.ENGINE_SCOPE);
   113             
   114             sb.append("\nfunction initVM() {"
   115                 + "\n  return bck2brwsr("
   116                 + "\n    function(name) { return loader.get(name);}"
   117                 + "\n  );"
   118                 + "\n};");
   119 
   120             Object res = js.eval(sb.toString());
   121             Assert.assertTrue(js instanceof Invocable, "It is invocable object: " + res);
   122             code = (Invocable) js;
   123             codeSeq = sb;
   124             compiled.put(clazz, new Object[] { code, codeSeq });
   125         }
   126 
   127         @Test(groups = "run") public void executeCode() throws Throwable {
   128             if (js) {
   129                 try {
   130                     compileTheCode(m.getDeclaringClass());
   131                     Object vm = code.invokeFunction("initVM");
   132                     Object inst = code.invokeMethod(vm, "loadClass", m.getDeclaringClass().getName());
   133                     value = code.invokeMethod(inst, m.getName() + "__" + computeSignature(m));
   134                 } catch (Exception ex) {
   135                     throw new AssertionError(StaticMethodTest.dumpJS(codeSeq)).initCause(ex);
   136                 }
   137             } else {
   138                 value = m.invoke(m.getDeclaringClass().newInstance());
   139             }
   140         }
   141         @Override
   142         public String getTestName() {
   143             return m.getName() + (js ? "[JavaScript]" : "[Java]");
   144         }
   145         
   146         private static String computeSignature(Method m) {
   147             StringBuilder sb = new StringBuilder();
   148             appendType(sb, m.getReturnType());
   149             for (Class<?> c : m.getParameterTypes()) {
   150                 appendType(sb, c);
   151             }
   152             return sb.toString();
   153         }
   154         
   155         private static void appendType(StringBuilder sb, Class<?> t) {
   156             if (t == null) {
   157                 sb.append('V');
   158                 return;
   159             }
   160             if (t.isPrimitive()) {
   161                 int ch = -1;
   162                 if (t == int.class) {
   163                     ch = 'I';
   164                 }
   165                 if (t == short.class) {
   166                     ch = 'S';
   167                 }
   168                 if (t == byte.class) {
   169                     ch = 'B';
   170                 }
   171                 if (t == boolean.class) {
   172                     ch = 'Z';
   173                 }
   174                 if (t == long.class) {
   175                     ch = 'J';
   176                 }
   177                 if (t == float.class) {
   178                     ch = 'F';
   179                 }
   180                 if (t == double.class) {
   181                     ch = 'D';
   182                 }
   183                 assert ch != -1 : "Unknown primitive type " + t;
   184                 sb.append((char)ch);
   185                 return;
   186             }
   187             if (t.isArray()) {
   188                 sb.append("_3");
   189                 appendType(sb, t.getComponentType());
   190                 return;
   191             }
   192             sb.append('L');
   193             sb.append(t.getName().replace('.', '_'));
   194             sb.append("_2");
   195         }
   196     }
   197 }