From d5db7270beb28e326dc9bc25462f0f06f6e70d67 Mon Sep 17 00:00:00 2001
From: Alexey Andreev <konsoletyper@gmail.com>
Date: Sat, 22 Apr 2017 00:26:43 +0300
Subject: [PATCH] Move expected exception handling off test runner. Implement
 it in launcher genertor instead

---
 tests/src/test/js/run.sh                      |  4 +++
 .../java/org/teavm/junit/TeaVMTestRunner.java | 10 ++-----
 .../junit/TestEntryPointTransformer.java      | 30 +++++++++++++++++--
 .../main/java/org/teavm/junit/TestRun.java    | 11 +------
 .../main/java/org/teavm/junit/TestRunner.java | 27 +----------------
 5 files changed, 37 insertions(+), 45 deletions(-)
 create mode 100755 tests/src/test/js/run.sh

diff --git a/tests/src/test/js/run.sh b/tests/src/test/js/run.sh
new file mode 100755
index 000000000..44fa483af
--- /dev/null
+++ b/tests/src/test/js/run.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+npm run build
+node start.js ../../../target/js-tests/
diff --git a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java
index 338989c30..324259230 100644
--- a/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java
+++ b/tools/junit/src/main/java/org/teavm/junit/TeaVMTestRunner.java
@@ -229,13 +229,9 @@ public class TeaVMTestRunner extends Runner implements Filterable {
 
             for (TeaVMTestConfiguration configuration : configurations) {
                 try {
-                    TestRun run = compileByTeaVM(child, notifier, expectedExceptions, configuration, onSuccess.get(0));
+                    TestRun run = compileByTeaVM(child, notifier, configuration, onSuccess.get(0));
                     if (run != null) {
                         runs.add(run);
-                    } else {
-                        notifier.fireTestFinished(description);
-                        latch.countDown();
-                        return;
                     }
                 } catch (Throwable e) {
                     notifier.fireTestFailure(new Failure(description, e));
@@ -293,7 +289,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
         return true;
     }
 
-    private TestRun compileByTeaVM(Method child, RunNotifier notifier, Set<Class<?>> expectedExceptions,
+    private TestRun compileByTeaVM(Method child, RunNotifier notifier,
             TeaVMTestConfiguration configuration, Consumer<Boolean> onComplete) {
         Description description = describeChild(child);
 
@@ -330,7 +326,7 @@ public class TeaVMTestRunner extends Runner implements Filterable {
 
         return new TestRun(compileResult.file.getParentFile(), child,
                 new MethodReference(testClass.getName(), getDescriptor(child)),
-                description, callback, expectedExceptions);
+                description, callback);
     }
 
     private void submitRun(TestRun run) {
diff --git a/tools/junit/src/main/java/org/teavm/junit/TestEntryPointTransformer.java b/tools/junit/src/main/java/org/teavm/junit/TestEntryPointTransformer.java
index ae1c27cc9..b089b40ac 100644
--- a/tools/junit/src/main/java/org/teavm/junit/TestEntryPointTransformer.java
+++ b/tools/junit/src/main/java/org/teavm/junit/TestEntryPointTransformer.java
@@ -15,14 +15,20 @@
  */
 package org.teavm.junit;
 
+import org.junit.Test;
 import org.teavm.diagnostics.Diagnostics;
+import org.teavm.model.AnnotationReader;
+import org.teavm.model.AnnotationValue;
+import org.teavm.model.BasicBlock;
 import org.teavm.model.ClassHolder;
 import org.teavm.model.ClassHolderTransformer;
 import org.teavm.model.ClassReaderSource;
 import org.teavm.model.ElementModifier;
 import org.teavm.model.MethodHolder;
+import org.teavm.model.MethodReader;
 import org.teavm.model.MethodReference;
 import org.teavm.model.Program;
+import org.teavm.model.TryCatchBlock;
 import org.teavm.model.ValueType;
 import org.teavm.model.emit.ProgramEmitter;
 import org.teavm.model.emit.ValueEmitter;
@@ -33,7 +39,7 @@ class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
     private String runnerClassName;
     private MethodReference testMethod;
 
-    public TestEntryPointTransformer(String runnerClassName, MethodReference testMethod) {
+    TestEntryPointTransformer(String runnerClassName, MethodReference testMethod) {
         this.runnerClassName = runnerClassName;
         this.testMethod = testMethod;
     }
@@ -75,7 +81,27 @@ class TestEntryPointTransformer implements ClassHolderTransformer, TeaVMPlugin {
         pe.getField(TestEntryPoint.class, "testCase", Object.class)
                 .cast(ValueType.object(testMethod.getClassName()))
                 .invokeSpecial(testMethod);
-        pe.exit();
+
+        MethodReader testMethodReader = innerSource.resolve(testMethod);
+        AnnotationReader testAnnotation = testMethodReader.getAnnotations().get(Test.class.getName());
+        AnnotationValue throwsValue = testAnnotation.getValue("expected");
+        if (throwsValue != null) {
+            BasicBlock handler = pe.getProgram().createBasicBlock();
+            TryCatchBlock tryCatch = new TryCatchBlock();
+            tryCatch.setExceptionType(((ValueType.Object) throwsValue.getJavaClass()).getClassName());
+            tryCatch.setHandler(handler);
+            pe.getBlock().getTryCatchBlocks().add(tryCatch);
+
+            BasicBlock nextBlock = pe.getProgram().createBasicBlock();
+            pe.jump(nextBlock);
+            pe.enter(nextBlock);
+            pe.construct(AssertionError.class, pe.constant("Expected exception not thrown")).raise();
+
+            pe.enter(handler);
+            pe.exit();
+        } else {
+            pe.exit();
+        }
         return pe.getProgram();
     }
 }
diff --git a/tools/junit/src/main/java/org/teavm/junit/TestRun.java b/tools/junit/src/main/java/org/teavm/junit/TestRun.java
index 54299debc..2487f2636 100644
--- a/tools/junit/src/main/java/org/teavm/junit/TestRun.java
+++ b/tools/junit/src/main/java/org/teavm/junit/TestRun.java
@@ -17,9 +17,6 @@ package org.teavm.junit;
 
 import java.io.File;
 import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
 import org.junit.runner.Description;
 import org.teavm.model.MethodReference;
 
@@ -29,16 +26,14 @@ class TestRun {
     private MethodReference reference;
     private Description description;
     private TestRunCallback callback;
-    private Set<Class<?>> expectedExceptions;
 
     TestRun(File baseDirectory, Method method, MethodReference reference, Description description,
-            TestRunCallback callback, Set<Class<?>> expectedExceptions) {
+            TestRunCallback callback) {
         this.baseDirectory = baseDirectory;
         this.method = method;
         this.reference = reference;
         this.description = description;
         this.callback = callback;
-        this.expectedExceptions = Collections.unmodifiableSet(new HashSet<>(expectedExceptions));
     }
 
     public File getBaseDirectory() {
@@ -60,8 +55,4 @@ class TestRun {
     public TestRunCallback getCallback() {
         return callback;
     }
-
-    public Set<Class<?>> getExpectedExceptions() {
-        return expectedExceptions;
-    }
 }
diff --git a/tools/junit/src/main/java/org/teavm/junit/TestRunner.java b/tools/junit/src/main/java/org/teavm/junit/TestRunner.java
index 0b74398ae..250e4b00d 100644
--- a/tools/junit/src/main/java/org/teavm/junit/TestRunner.java
+++ b/tools/junit/src/main/java/org/teavm/junit/TestRunner.java
@@ -33,10 +33,6 @@ class TestRunner {
         this.strategy = strategy;
     }
 
-    public int getNumThreads() {
-        return numThreads;
-    }
-
     public void setNumThreads(int numThreads) {
         this.numThreads = numThreads;
     }
@@ -100,32 +96,11 @@ class TestRunner {
             String status = resultObject.get("status").asText();
             switch (status) {
                 case "ok":
-                    if (!run.getExpectedExceptions().isEmpty()) {
-                        run.getCallback().error(new AssertionError("Expected exception was not thrown"));
-                    } else {
-                        run.getCallback().complete();
-                    }
+                    run.getCallback().complete();
                     break;
                 case "exception": {
                     String stack = resultObject.get("stack").asText();
                     String exception = resultObject.has("exception") ? resultObject.get("exception").asText() : null;
-                    Class<?> exceptionClass;
-                    if (exception != null) {
-                        try {
-                            exceptionClass = Class.forName(exception, false, TestRunner.class.getClassLoader());
-                        } catch (ClassNotFoundException e) {
-                            exceptionClass = null;
-                        }
-                    } else {
-                        exceptionClass = null;
-                    }
-                    if (exceptionClass != null) {
-                        Class<?> caught = exceptionClass;
-                        if (run.getExpectedExceptions().stream().anyMatch(e -> e.isAssignableFrom(caught))) {
-                            run.getCallback().complete();
-                            break;
-                        }
-                    }
                     run.getCallback().error(new AssertionError(exception + "\n" + stack));
                     break;
                 }