/*
 * Copyright 2000-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.openapi.editor.impl;

import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.FoldRegion;
import com.intellij.openapi.editor.FoldingModel;
import com.intellij.openapi.editor.ex.DocumentEx;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class EditorStressTest extends AbstractEditorTest {
  private static final int ITERATIONS = 10000;
  private static final Long SEED_OVERRIDE = null; // set non-null value to run with a specific seed

  private static final List<? extends Action> ourActions = Arrays.asList(new AddText("a"),
                                                                         new AddText("\n"),
                                                                         new AddText("\t"),
                                                                         new RemoveCharacter(),
                                                                         new MoveCharacter(),
                                                                         new AddFoldRegion(),
                                                                         new RemoveFoldRegion(),
                                                                         new CollapseFoldRegion(),
                                                                         new ExpandFoldRegion());

  private final Random myRandom = new Random() {{
    //noinspection ConstantConditions
    setSeed(mySeed = (SEED_OVERRIDE == null ? nextLong() : SEED_OVERRIDE));
  }};
  private long mySeed;

  public void testRandomActions() {
    int i = 0;
    try {
      initText("");
      configureSoftWraps(10);
      for (i = 1; i <= ITERATIONS; i++) {
        doRandomAction();
        checkConsistency(myEditor);
      }
    }
    catch (Throwable t) {
      throw new RuntimeException("Failed when run with seed=" + mySeed + " in iteration " + i, t);
    }
  }

  private void doRandomAction() {
    ourActions.get(myRandom.nextInt(ourActions.size())).perform(myEditor, myRandom);
  }

  protected void checkConsistency(Editor editor) {
  }

  interface Action {
    void perform(Editor editor, Random random);
  }

  private static class AddText implements Action {
    private final String myText;

    AddText(String text) {
      myText = text;
    }

    @Override
    public void perform(Editor editor, Random random) {
      Document document = editor.getDocument();
      int offset = random.nextInt(document.getTextLength() + 1);
      document.insertString(offset, myText);
    }
  }

  private static class RemoveCharacter implements Action {
    @Override
    public void perform(Editor editor, Random random) {
      Document document = editor.getDocument();
      int textLength = document.getTextLength();
      if (textLength <= 0) return;
      int offset = random.nextInt(textLength);
      document.deleteString(offset, offset + 1);
    }
  }

  private static class MoveCharacter implements Action {
    @Override
    public void perform(Editor editor, Random random) {
      Document document = editor.getDocument();
      int textLength = document.getTextLength();
      if (textLength <= 0) return;
      int offset = random.nextInt(textLength);
      int targetOffset = random.nextInt(textLength + 1);
      if (targetOffset < offset || targetOffset > offset + 1) {
        ((DocumentEx)document).moveText(offset, offset + 1, targetOffset);
      }
    }
  }

  private static class AddFoldRegion implements Action {
    @Override
    public void perform(final Editor editor, Random random) {
      Document document = editor.getDocument();
      int textLength = document.getTextLength();
      if (textLength <= 0) return;
      final int startOffset = random.nextInt(textLength + 1);
      final int endOffset = random.nextInt(textLength + 1);
      if (startOffset == endOffset) return;
      final FoldingModel foldingModel = editor.getFoldingModel();
      foldingModel.runBatchFoldingOperation(() -> foldingModel.addFoldRegion(Math.min(startOffset, endOffset),
                                                                             Math.max(startOffset, endOffset),
                                                                             "."));
    }
  }

  private static class RemoveFoldRegion implements Action {
    @Override
    public void perform(final Editor editor, Random random) {
      final FoldingModel foldingModel = editor.getFoldingModel();
      FoldRegion[] foldRegions = foldingModel.getAllFoldRegions();
      if (foldRegions.length == 0) return;
      final FoldRegion region = foldRegions[random.nextInt(foldRegions.length)];
      foldingModel.runBatchFoldingOperation(() -> foldingModel.removeFoldRegion(region));
    }
  }

  private static class CollapseFoldRegion implements Action {
    @Override
    public void perform(final Editor editor, Random random) {
      final FoldingModel foldingModel = editor.getFoldingModel();
      FoldRegion[] foldRegions = foldingModel.getAllFoldRegions();
      if (foldRegions.length == 0) return;
      final FoldRegion region = foldRegions[random.nextInt(foldRegions.length)];
      foldingModel.runBatchFoldingOperation(() -> region.setExpanded(false));
    }
  }

  private static class ExpandFoldRegion implements Action {
    @Override
    public void perform(final Editor editor, Random random) {
      final FoldingModel foldingModel = editor.getFoldingModel();
      FoldRegion[] foldRegions = foldingModel.getAllFoldRegions();
      if (foldRegions.length == 0) return;
      final FoldRegion region = foldRegions[random.nextInt(foldRegions.length)];
      foldingModel.runBatchFoldingOperation(() -> region.setExpanded(true));
    }
  }
}
@@ -2466,35 +2466,25 @@
     
-throw new RuntimeException(
+String message = 
 %22Fai
@@ -2539,16 +2539,93 @@
 on %22 + i
+;%0A      System.out.println(message);%0A      throw new RuntimeException(message
 , t);%0A  

make sure seed and iteration are always logged, even if thrown exception is discarded upper in the call stack