package fitnesse.junit;

import fitnesse.testrunner.TestsRunnerListener;
import fitnesse.testsystems.Assertion;
import fitnesse.testsystems.ExceptionResult;
import fitnesse.testsystems.ExecutionResult;
import fitnesse.testsystems.TestResult;
import fitnesse.testsystems.TestSummary;
import fitnesse.testsystems.TestSystem;
import fitnesse.testrunner.WikiTestPage;
import fitnesse.testsystems.TestSystemListener;
import org.junit.runner.Description;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunNotifier;

import java.io.IOException;

public class JUnitRunNotifierResultsListener implements TestSystemListener<WikiTestPage>, TestsRunnerListener {

  private final Class<?> mainClass;
  private final RunNotifier notifier;
  private int totalNumberOfTests;
  private int completedTests;
  private Throwable firstFailure;

  public JUnitRunNotifierResultsListener(RunNotifier notifier, Class<?> mainClass) {
    this.notifier = notifier;
    this.mainClass = mainClass;
  }

  @Override
  public void announceNumberTestsToRun(int testsToRun) {
    totalNumberOfTests = testsToRun;
  }

  @Override
  public void unableToStartTestSystem(String testSystemName, Throwable cause) throws IOException {
    notifyOfTestSystemException(testSystemName, cause);
  }

  @Override
  public void testStarted(WikiTestPage test) {
    firstFailure = null;
    if (test.isTestPage()) {
      notifier.fireTestStarted(descriptionFor(test));
    }
  }

  @Override
  public void testComplete(WikiTestPage test, TestSummary testSummary) {
    completedTests++;
    if (firstFailure != null) {
      notifier.fireTestFailure(new Failure(descriptionFor(test), firstFailure));
    } else if (test.isTestPage()) {
      if (testSummary.getExceptions() > 0) {
        notifier.fireTestFailure(new Failure(descriptionFor(test), new Exception("Exception occurred on page " + test.getFullPath())));
      } else if (testSummary.getWrong() > 0) {
        notifier.fireTestFailure(new Failure(descriptionFor(test), new AssertionError("Test failures occurred on page " + test.getFullPath())));
      } else {
        notifier.fireTestFinished(descriptionFor(test));
      }
    }
  }

  @Override
  public void testOutputChunk(String output) {
  }

  @Override
  public void testAssertionVerified(Assertion assertion, TestResult testResult) {
    if (testResult != null &&
            testResult.doesCount() &&
            (testResult.getExecutionResult() == ExecutionResult.FAIL ||
                    testResult.getExecutionResult() == ExecutionResult.ERROR)) {
      firstFailure(testResult.getExecutionResult(), createMessage(testResult));
    }
  }

  @Override
  public void testExceptionOccurred(Assertion assertion, ExceptionResult exceptionResult) {
    firstFailure(exceptionResult.getExecutionResult(), exceptionResult.getMessage());
  }

  @Override
  public void testSystemStarted(TestSystem testSystem) {
  }

  @Override
  public void testSystemStopped(TestSystem testSystem, Throwable cause) {
    notifyOfTestSystemException(testSystem.getName(), cause);
  }

  protected void notifyOfTestSystemException(String testSystemName, Throwable cause) {
    Throwable t = cause;
    if (completedTests != totalNumberOfTests) {
      String msg = String.format(
                            "Unable to complete suite. Error in test system %s. Completed %s of %s tests.",
                            testSystemName, completedTests, totalNumberOfTests);
      t = new Exception(msg, cause);
    }

    if (t != null) {
      notifier.fireTestFailure(new Failure(Description.createSuiteDescription(mainClass), t));
    }
  }

  private Description descriptionFor(WikiTestPage test) {
    return Description.createTestDescription(mainClass, test.getFullPath());
  }

  String createMessage(TestResult testResult) {
    if (testResult.hasActual() && testResult.hasExpected()) {
      return String.format("[%s] expected [%s]",
              testResult.getActual(),
              testResult.getExpected());
    } else if ((testResult.hasActual() || testResult.hasExpected()) && testResult.hasMessage()) {
      return String.format("[%s] %s",
              testResult.hasActual() ? testResult.getActual() : testResult.getExpected(),
              testResult.getMessage());
    }
    return testResult.getMessage();
  }

  private void firstFailure(ExecutionResult executionResult, String message) {
    if (firstFailure != null) {
      return;
    }
    if (executionResult == ExecutionResult.ERROR) {
      firstFailure = new Exception(message);
    } else {
      firstFailure = new AssertionError(message);
    }
  }
}

@@ -29,47 +29,143 @@
 ort 
-fitnesse.testrunner.TestsRunnerListener
+java.io.Closeable;%0Aimport java.io.IOException;%0A%0Aimport fitnesse.testrunner.TestsRunnerListener;%0Aimport fitnesse.testrunner.WikiTestPage
 ;%0Aim
@@ -416,49 +416,8 @@
 em;%0A
-import fitnesse.testrunner.WikiTestPage;%0A
 impo
@@ -598,37 +598,8 @@
 r;%0A%0A
-import java.io.IOException;%0A%0A
 publ
@@ -638,16 +638,24 @@
 Listener
+%0A       
  impleme
@@ -711,16 +711,27 @@
 Listener
+, Closeable
  %7B%0A%0A  pr
@@ -3130,118 +3130,42 @@
 %0A%0A  
-protected void notifyOfTestSystemException(String testSystemName, Throwable cau
+@Override%0A  public void clo
 se
+(
 ) %7B%0A
-    Throwable t = cause;%0A
     
@@ -3260,72 +3260,31 @@
     
-              %22Unable to complete suite. Error in test system %25s
+%22Not all tests executed
 . Co
@@ -3326,38 +3326,8 @@
     
-               testSystemName,
  com
@@ -3366,17 +3366,27 @@
 ;%0A      
-t
+Exception e
  = new E
@@ -3401,45 +3401,330 @@
 (msg
-, cause);%0A    %7D%0A%0A    if (t != null) %7B
+);%0A      notifier.fireTestFailure(new Failure(Description.createSuiteDescription(mainClass), e));%0A    %7D%0A  %7D%0A%0A  protected void notifyOfTestSystemException(String testSystemName, Throwable cause) %7B%0A    if (cause != null) %7B%0A      Exception e = new Exception(%22Exception while executing tests using: %22 + testSystemName, cause);
 %0A   
@@ -3810,17 +3810,17 @@
 Class), 
-t
+e
 ));%0A    

Fix for #762, only check total number of tests after all test systems have been executed