package com.google.gwt.reflect.test; import xapi.gwt.junit.impl.JUnit4Executor; import static com.google.gwt.reflect.shared.GwtReflect.magicClass; import com.google.gwt.core.client.Callback; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.RunAsyncCallback; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.BodyElement; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Overflow; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.dom.client.Style.VerticalAlign; import com.google.gwt.reflect.client.ConstPool; import com.google.gwt.reflect.shared.JsMemberPool; import com.google.gwt.reflect.shared.ReflectUtil; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.EventListener; import com.google.gwt.user.client.Timer; import java.lang.reflect.Method; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; @SuppressWarnings("deprecation" ) public class TestEntryPoint implements EntryPoint { private static final String TEST_RESULTS = "test.result"; private final Map<Method,Object> tests = new LinkedHashMap<Method,Object>(); private final Map<Class<?>,Method[]> testClasses = new LinkedHashMap<Class<?>,Method[]>(); @Override public void onModuleLoad() { new Timer() { @Override public void run() { go(); } }.schedule(10); } public void go() { final String module = GWT.getModuleName(), host = GWT.getHostPageBaseURL().replace("/"+module, ""); print("<a href='#' onclick=\"" + "window.__gwt_bookmarklet_params = " + "{server_url:'" + host+ "', " + "module_name:'" + module + "'}; " + "var s = document.createElement('script'); " + "s.src = 'http://localhost:1337/dev_mode_on.js'; " + "document.getElementsByTagName('head')[0].appendChild(s); " + "return true;" + "\">Recompile</a>", null); print("<style>" + "h3 {" + "margin-bottom: 5px;" + "}" + ".results {" + "color: grey;" + " margin-bottom: 5px;" + " text-align: center;" + "}" + ".success {" + "color: green;" + "}" + ".fail {" + "color: red;" + "}" + "</style>", null); GWT.runAsync(TestEntryPoint.class, new RunAsyncCallback() { @Override public void onSuccess() { try { String.class.getMethod("equals", Object.class).invoke("!", "!"); } catch (final Exception e) {print("Basic string reflection not working; " + "expect failures...", e); } // Do not change the order of the following calls unless you also // update the initial-load-sequence defined in CompileSizeTest.gwt.xml loadTests(false); addArrayTests(); addAnnotationTests(); addConstructorTests(); addMethodTests(); addFieldTests(); ConstPool.loadConstPool(new Callback<ConstPool, Throwable>() { @Override public void onSuccess(final ConstPool result) { for (final JsMemberPool<?> m : result.getAllReflectionData()) { try { final Class<?> c = m.getType(); if (!testClasses.containsKey(c)) { addTests(c); } } catch (final Throwable e) { print("Error adding tests", e); } } loadTests(true); } @Override public void onFailure(final Throwable caught) { print("Error loading ConstPool", caught); } }); } @Override public void onFailure(final Throwable reason) { print("Error loading TestEntryPoint", reason); } }); } protected void loadTests(final boolean forReal) { GWT.runAsync(JUnit4Executor.class, new RunAsyncCallback() { @Override public void onSuccess() { if (forReal) { displayTests(); runTests(); } } @Override public void onFailure(final Throwable reason) { } }); } protected void addAnnotationTests() { GWT.runAsync(AnnotationTests.class, new RunAsyncCallback() { @Override public void onSuccess() { magicClass(AnnotationTests.class); try { addTests(AnnotationTests.class); } catch (final Throwable e) { print("Error adding AnnotationTests", e); } } @Override public void onFailure(final Throwable reason) { print("Error loading AnnotationTests", reason); } }); } protected void addArrayTests() { GWT.runAsync(ArrayTests.class, new RunAsyncCallback() { @Override public void onSuccess() { magicClass(ArrayTests.class); try { addTests(ArrayTests.class); } catch (final Throwable e) { print("Error adding ArrayTests", e); } } @Override public void onFailure(final Throwable reason) { print("Error loading ArrayTests", reason); } }); } protected void addConstructorTests() { GWT.runAsync(ConstructorTests.class, new RunAsyncCallback() { @Override public void onSuccess() { magicClass(ConstructorTests.class); try { addTests(ConstructorTests.class); } catch (final Throwable e) { print("Error adding ConstructorTests", e); } } @Override public void onFailure(final Throwable reason) { print("Error loading ConstructorTests", reason); } }); } protected void addFieldTests() { GWT.runAsync(FieldTests.class, new RunAsyncCallback() { @Override public void onSuccess() { magicClass(FieldTests.class); try { addTests(FieldTests.class); } catch (final Throwable e) { print("Error adding FieldTests", e); } } @Override public void onFailure(final Throwable reason) { print("Error loading FieldTests", reason); } }); } protected void addMethodTests() { GWT.runAsync(MethodTests.class, new RunAsyncCallback() { @Override public void onSuccess() { magicClass(MethodTests.class).getMethods(); try { addTests(MethodTests.class); } catch (final Throwable e) { print("Error adding MethodTests", e); } } @Override public void onFailure(final Throwable reason) { print("Error loading MethodTests", reason); } }); } public void addTests(final Class<?> cls) throws Throwable { final Method[] allTests = JUnit4Executor.findTests(cls); if (allTests.length > 0) { testClasses.put(cls, allTests); final Object inst = cls.newInstance(); for (final Method method : allTests) { tests.put(method, inst); } } } private void displayTests() { final BodyElement body = Document.get().getBody(); for (final Class<?> c : testClasses.keySet()) { final DivElement div = Document.get().createDivElement(); div.getStyle().setDisplay(Display.INLINE_BLOCK); div.getStyle().setVerticalAlign(VerticalAlign.TOP); div.getStyle().setMarginRight(2, Unit.EM); div.getStyle().setProperty("maxHeight", "400px"); div.getStyle().setOverflowY(Overflow.AUTO); StringBuilder b = new StringBuilder(); final String id = toId(c); b .append("<h3><a id='"+id+ "' href='#run:"+id+ "'>") .append(c.getName()) .append("</a></h3>") .append("<div class='results' id='"+TEST_RESULTS+id+"'> </div>") ; try { final String path = c.getProtectionDomain().getCodeSource().getLocation().getPath(); b.append("<sup><a href='file://"+path+"'>") .append(path) .append("</a></sup>"); } catch (final Exception ignored) {} div.setInnerHTML(b.toString()); for (final Method m : testClasses.get(c)) { final String methodId = m.getName()+c.hashCode(); b = new StringBuilder(); b.append("<pre>"); b.append("<a href='javascript:'>"); b.append(m.getName()); b.append("</a>"); b.append('('); b.append(ReflectUtil.joinClasses(", ", m.getParameterTypes())); b.append(')'); b.append("</pre>"); b.append("<div id='"+methodId+"'> </div>"); final Element el = Document.get().createDivElement().cast(); el.setInnerHTML(b.toString()); DOM.setEventListener(el, new EventListener() { @Override public void onBrowserEvent(final Event event) { if (event.getTypeInt() == Event.ONCLICK) { runTest(m); } } }); DOM.sinkEvents(el, Event.ONCLICK); div.appendChild(el); } body.appendChild(div); final Element anchor = Document.get().getElementById(id).cast(); DOM.setEventListener(anchor, new EventListener() { @Override public void onBrowserEvent(final Event event) { final Map<Method, Boolean> res = testResults.get(c); for (final Method m : res.keySet().toArray(new Method[res.size()])) { res.put(m, null); } updateTestClass(c); for (final Method m : testClasses.get(c)) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { runTest(m); } }); } } }); DOM.sinkEvents(anchor, Event.ONCLICK); } } private native void log(Object o) /*-{ $wnd.console && $wnd.console.log(o); }-*/; private String toId(final Class<?> c) { return c.getName().replace('.', '_'); } Map<Class<?>, Map<Method, Boolean>> testResults = new LinkedHashMap<Class<?>, Map<Method, Boolean>>(); private void runTests() { int delay = 1; testResults.clear(); for (final Method method : tests.keySet()) { Map<Method, Boolean> results = testResults.get(method.getDeclaringClass()); if (results == null) { results = new HashMap<Method, Boolean>(); testResults.put(method.getDeclaringClass(), results); } results.put(method, null); new Timer() { @Override public void run() { runTest(method); } }.schedule(delay += 5); } for (final Class<?> testClass : testResults.keySet()) { updateTestClass(testClass); } } private void updateTestClass(final Class<?> cls) { final String id = toId(cls); final Element el = DOM.getElementById(TEST_RESULTS+id); final Map<Method, Boolean> results = testResults.get(cls); int success = 0, fail = 0; final int total = results.size(); for (final Entry<Method, Boolean> e : results.entrySet()) { if (e.getValue() != null) { if (e.getValue()) { success ++; } else { fail ++; } } } final StringBuilder b = new StringBuilder("<span class='success'>Passed: ") .append(success).append("/").append(total).append("</span>; ") .append("<span"); if (fail > 0) { b.append(" class='fail'"); } b.append(">Failed: ").append(fail).append("/").append(total); el.setInnerHTML(b.toString()); } protected void runTest(final Method m) { final String id = m.getName()+m.getDeclaringClass().hashCode(); final com.google.gwt.dom.client.Element el = Document.get().getElementById(id); el.setInnerHTML(""); final Map<Method, Boolean> results = testResults.get(m.getDeclaringClass()); try { JUnit4Executor.runTest(tests.get(m), m); results.put(m, true); debug(el, "<div style='color:green'>" + m.getName() + " passes!</div>", null); } catch (Throwable e) { results.put(m, false); final String error = m.getDeclaringClass().getName() + "." + m.getName() + " failed"; while (e.getClass() == RuntimeException.class && e.getCause() != null) { e = e.getCause(); } debug(el, error, e); try { // Move the element up to the top of the results list. final com.google.gwt.dom.client.Element result = el.getParentElement(); final com.google.gwt.dom.client.Element parent = result.getParentElement(); parent.insertAfter(result, parent.getChild(2)); } catch(final Exception ignored){} if (e instanceof Error) { throw (Error)e; } if (e instanceof RuntimeException) { throw (Error)e; } throw new AssertionError(error); } finally { updateTestClass(m.getDeclaringClass()); } } private void print(final String string, final Throwable e) { final DivElement el = Document.get().createDivElement(); debug(el, string, e); Document.get().getBody().appendChild(el); } private void debug(final com.google.gwt.dom.client.Element el, final String string, Throwable e) { final StringBuilder b = new StringBuilder(); b.append(string); b.append('\n'); b.append("<pre style='color:red;'>"); while (e != null) { b.append(e); b.append('\n'); for (final StackTraceElement trace : e.getStackTrace()) { b.append('\t') .append(trace.getClassName()) .append('.') .append(trace.getMethodName()) .append(' ') .append(trace.getFileName()) .append(':') .append(trace.getLineNumber()) .append('\n'); } e = e.getCause(); } b.append("</pre>"); el.setInnerHTML(b.toString()); } }