vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/VMTest.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 23 Dec 2012 17:02:34 +0100
branchlauncher
changeset 370 ed48023d1d85
parent 360 86f3ea771e24
child 372 3485327d3080
permissions -rw-r--r--
JavaScript and HTTP launchers separated
     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.bck2brwsr.vmtest;
    19 
    20 import java.io.File;
    21 import java.io.FileWriter;
    22 import java.io.IOException;
    23 import java.lang.reflect.Method;
    24 import java.util.Map;
    25 import java.util.WeakHashMap;
    26 import java.util.logging.Level;
    27 import java.util.logging.Logger;
    28 import javax.script.Invocable;
    29 import org.apidesign.bck2brwsr.launcher.MethodInvocation;
    30 import org.testng.Assert;
    31 import org.testng.ITest;
    32 import org.testng.annotations.Factory;
    33 import org.testng.annotations.Test;
    34 
    35 /** A TestNG {@link Factory} that seeks for {@link Compare} annotations
    36  * in provided class and builds set of tests that compare the computations
    37  * in real as well as JavaScript virtual machines. Use as:<pre>
    38  * {@code @}{@link Factory} public static create() {
    39  *   return @{link VMTest}.{@link #create(YourClass.class);
    40  * }</pre>
    41  *
    42  * @author Jaroslav Tulach <jtulach@netbeans.org>
    43  */
    44 public final class VMTest implements ITest {
    45     private static final Launcher JS = new Launcher("js");
    46     private static final Launcher BROWSER = new Launcher();
    47     
    48     private final Run first, second;
    49     private final Method m;
    50     
    51     private VMTest(Method m, Run first, Run second) {
    52         this.first = first;
    53         this.second = second;
    54         this.m = m;
    55     }
    56 
    57     /** Inspects <code>clazz</code> and for each {@lik Compare} method creates
    58      * instances of tests. Each instance runs the test in different virtual
    59      * machine and at the end they compare the results.
    60      * 
    61      * @param clazz the class to inspect
    62      * @return the set of created tests
    63      */
    64     public static Object[] create(Class<?> clazz) {
    65         Method[] arr = clazz.getMethods();
    66         Object[] ret = new Object[5 * arr.length];
    67         int cnt = 0;
    68         for (Method m : arr) {
    69             Compare c = m.getAnnotation(Compare.class);
    70             if (c == null) {
    71                 continue;
    72             }
    73             final Run real = new Run(m, 0);
    74             final Run js = new Run(m, 1);
    75             final Run brwsr = new Run(m, 2);
    76             ret[cnt++] = real;
    77             ret[cnt++] = js;
    78             ret[cnt++] = brwsr;
    79             ret[cnt++] = new VMTest(m, real, js);
    80             ret[cnt++] = new VMTest(m, real, brwsr);
    81         }
    82         Object[] r = new Object[cnt];
    83         for (int i = 0; i < cnt; i++) {
    84             r[i] = ret[i];
    85         }
    86         return r;
    87     }
    88 
    89     /** Test that compares the previous results.
    90      * @throws Throwable 
    91      */
    92     @Test(dependsOnGroups = "run") public void compareResults() throws Throwable {
    93         Object v1 = first.value;
    94         Object v2 = second.value;
    95         if (v1 != null) {
    96             v1 = v1.toString();
    97         } else {
    98             v1 = "null";
    99         }
   100         Assert.assertEquals(v2, v1, "Comparing results");
   101     }
   102     
   103     /** Test name.
   104      * @return name of the tested method followed by a suffix
   105      */
   106     @Override
   107     public String getTestName() {
   108         return m.getName() + "[Compare " + second.typeName() + "]";
   109     }
   110 
   111     public static final class Run implements ITest {
   112         private final Method m;
   113         private final int type;
   114         Object value;
   115         private Invocable code;
   116         private CharSequence codeSeq;
   117         private static final Map<Class,Object[]> compiled = new WeakHashMap<>();
   118         private Object inst;
   119 
   120         private Run(Method m, int type) {
   121             this.m = m;
   122             this.type = type;
   123             try {
   124                 initialize();
   125             } catch (Throwable ex) {
   126                 Logger.getLogger(VMTest.class.getName()).log(Level.SEVERE, null, ex);
   127             }
   128         }
   129 
   130         private void initialize() throws Throwable {
   131             if (type == 1) {
   132                 inst = JS.addMethod(m.getDeclaringClass(), m.getName());
   133             }
   134             if (type == 2) {
   135                 inst = BROWSER.addMethod(m.getDeclaringClass(), m.getName());
   136             }
   137         }
   138 
   139         @Test(groups = "run") public void executeCode() throws Throwable {
   140             if (type == 1) {
   141                 MethodInvocation c = (MethodInvocation) inst;
   142                 JS.exec();
   143                 value = c.toString();
   144             } else if (type == 2) {
   145                 MethodInvocation c = (MethodInvocation) inst;
   146                 BROWSER.exec();
   147                 value = c.toString();
   148             } else {
   149                 value = m.invoke(m.getDeclaringClass().newInstance());
   150             }
   151         }
   152         @Override
   153         public String getTestName() {
   154             return m.getName() + "[" + typeName() + "]";
   155         }
   156         
   157         final String typeName() {
   158             switch (type) {
   159                 case 0: return "Java";
   160                 case 1: return "JavaScript";
   161                 case 2: return "Browser";
   162                 default: return "Unknown type " + type;
   163             }
   164         }
   165         
   166         private static String computeSignature(Method m) {
   167             StringBuilder sb = new StringBuilder();
   168             appendType(sb, m.getReturnType());
   169             for (Class<?> c : m.getParameterTypes()) {
   170                 appendType(sb, c);
   171             }
   172             return sb.toString();
   173         }
   174         
   175         private static void appendType(StringBuilder sb, Class<?> t) {
   176             if (t == null) {
   177                 sb.append('V');
   178                 return;
   179             }
   180             if (t.isPrimitive()) {
   181                 int ch = -1;
   182                 if (t == int.class) {
   183                     ch = 'I';
   184                 }
   185                 if (t == short.class) {
   186                     ch = 'S';
   187                 }
   188                 if (t == byte.class) {
   189                     ch = 'B';
   190                 }
   191                 if (t == boolean.class) {
   192                     ch = 'Z';
   193                 }
   194                 if (t == long.class) {
   195                     ch = 'J';
   196                 }
   197                 if (t == float.class) {
   198                     ch = 'F';
   199                 }
   200                 if (t == double.class) {
   201                     ch = 'D';
   202                 }
   203                 assert ch != -1 : "Unknown primitive type " + t;
   204                 sb.append((char)ch);
   205                 return;
   206             }
   207             if (t.isArray()) {
   208                 sb.append("_3");
   209                 appendType(sb, t.getComponentType());
   210                 return;
   211             }
   212             sb.append('L');
   213             sb.append(t.getName().replace('.', '_'));
   214             sb.append("_2");
   215         }
   216     }
   217     
   218     static StringBuilder dumpJS(CharSequence sb) throws IOException {
   219         File f = File.createTempFile("execution", ".js");
   220         try (FileWriter w = new FileWriter(f)) {
   221             w.append(sb);
   222         }
   223         return new StringBuilder(f.getPath());
   224     }
   225 }