package jetbrains.mps.testbench.junit;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import jetbrains.mps.testbench.util.CachingAppender;
import java.util.regex.Pattern;
import java.util.Map;
import org.junit.runner.Description;
import java.util.HashMap;
import jetbrains.mps.testbench.util.ThreadWatcher;
import org.junit.runner.notification.RunNotifier;
import java.util.List;
import java.util.ArrayList;
import org.junit.runner.notification.StoppedByUserException;
import org.junit.runner.notification.Failure;

/**
 * fyodor, Aug 18, 2010
 */
@GeneratedClass(node = "r:7eb6d2ae-f266-42d4-885f-016951b158e4(jetbrains.mps.testbench.junit)/5294483648489412205", model = "r:7eb6d2ae-f266-42d4-885f-016951b158e4(jetbrains.mps.testbench.junit)")
public class WatchingRunNotifier extends DelegatingRunNotifier {
  private static final CachingAppender.Level DEFAULT_WATCH_LOGGER_LEVEL = CachingAppender.Level.ERROR;
  private static final Pattern EXECUTION_LIMIT_FAILED_PATTERN = Pattern.compile("(\\d)* ms execution limit failed for:[^,]*,(\\d*)(\\s)*");
  private static final Pattern USER_PREF_DIR = Pattern.compile(".*INFO: Created user preferences directory.*");
  private static final Pattern WARN_PATTERN_MULTILINE = Pattern.compile("\\[([\\d\\s])*\\](\\s)*WARN.*:\\n[^\\[^\\s].*");
  private static final Pattern WARN_PATTERN = Pattern.compile("\\[([\\d\\s])*\\](\\s)*WARN.*");
  private static final Pattern MPS_31017 = Pattern.compile(".*java\\.util\\.prefs\\.FileSystemPreferences.*");
  private static final Pattern MPS_31017_2 = Pattern.compile("WARNING: Prefs file removed in background.*");

  private final IgnoringPatternErrorStream myErrorCachingStream;

  private final CachingAppender myCachingAppender;
  private final Map<Description, Object> myTestsToIgnore = new HashMap<Description, Object>();
  private ThreadWatcher myThreadWatcher;

  public WatchingRunNotifier(RunNotifier delegate) {
    this(delegate, DEFAULT_WATCH_LOGGER_LEVEL, true);
  }

  public WatchingRunNotifier(RunNotifier delegate, CachingAppender.Level watchLevel, boolean ignoreWarnings) {
    super(delegate);
    myCachingAppender = new CachingAppender(watchLevel);
    myErrorCachingStream = new IgnoringPatternErrorStream(getPatternsToIgnore(ignoreWarnings));
  }

  private List<Pattern> getPatternsToIgnore(boolean ignoreWarnings) {
    List<Pattern> result = new ArrayList<Pattern>();
    result.add(EXECUTION_LIMIT_FAILED_PATTERN);
    result.add(MPS_31017);
    result.add(MPS_31017_2);
    result.add(USER_PREF_DIR);
    if (ignoreWarnings) {
      result.add(WARN_PATTERN_MULTILINE);
      result.add(WARN_PATTERN);
    }
    return result;
  }

  public void dispose() {
  }

  @Override
  public void fireTestStarted(Description description) throws StoppedByUserException {
    super.fireTestStarted(description);
    try {
      beforeTest(description);
    } catch (Throwable e) {
      super.fireTestFailure(new Failure(description, e));
    }
  }

  @Override
  public void fireTestFinished(Description description) {
    try {
      afterTest(description);
    } catch (Throwable e) {
      super.fireTestFailure(new Failure(description, e));
    }
    super.fireTestFinished(description);
  }

  @Override
  public void fireTestFailure(Failure failure) {
    myTestsToIgnore.put(failure.getDescription(), Boolean.TRUE);
    super.fireTestFailure(failure);
  }

  @Override
  public void fireTestAssumptionFailed(Failure failure) {
    myTestsToIgnore.put(failure.getDescription(), Boolean.TRUE);
    super.fireTestAssumptionFailed(failure);
  }

  @Override
  public void fireTestIgnored(Description description) {
    myTestsToIgnore.put(description, Boolean.TRUE);
    super.fireTestIgnored(description);
  }

  private void beforeTest(Description desc) {
    myErrorCachingStream.reRoute();
    myCachingAppender.attach();

    ExpectLogEvent expectEvent = desc.getAnnotation(ExpectLogEvent.class);
    if (expectEvent != null) {
      for (String text : expectEvent.text()) {
        myCachingAppender.expectEvent(expectEvent.level(), text);
      }
    }

    IgnoreLogEvents ignoreEvents = desc.getAnnotation(IgnoreLogEvents.class);
    if (ignoreEvents != null) {
      myTestsToIgnore.put(desc, Boolean.TRUE);
    }

    myThreadWatcher = new ThreadWatcher(true);
  }

  private void afterTest(Description desc) {
    myThreadWatcher.waitUntilSettled(15000);
    myErrorCachingStream.reset();
    myCachingAppender.sealEvents();
    myCachingAppender.detach();
    Failure fail = null;
    if (!(myTestsToIgnore.containsKey(desc))) {
      if (myErrorCachingStream.isNotEmpty() || myCachingAppender.isNotEmpty() || myThreadWatcher.isNotEmpty()) {
        fail = new Failure(desc, new UncleanTestExecutionException(myErrorCachingStream, myCachingAppender, myThreadWatcher));
      }
    }
    myErrorCachingStream.clear();
    if (fail != null) {
      super.fireTestFailure(fail);
    }
  }
}
