package jetbrains.mps.lang.test.runtime;
/*Generated by MPS */
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import com.intellij.ide.DataManager;
import com.intellij.ide.impl.DataManagerImpl;
import jetbrains.mps.openapi.editor.EditorComponent;
import jetbrains.mps.openapi.editor.Editor;
import jetbrains.mps.ide.editor.MPSFileNodeEditor;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.ide.ThreadUtils;
import java.lang.reflect.InvocationTargetException;
import jetbrains.mps.nodeEditor.NodeEditorComponent;
import java.util.List;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import jetbrains.mps.lang.test.matcher.NodesMatcher;
import jetbrains.mps.lang.test.matcher.NodeDifference;
import java.util.Collections;
import junit.framework.Assert;
import java.util.Map;
import jetbrains.mps.testbench.util.CachingAppender;
import jetbrains.mps.testbench.junit.UncleanTestExecutionException;
import com.intellij.openapi.command.impl.UndoManagerImpl;
import com.intellij.openapi.command.undo.UndoManager;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.nodefs.MPSNodeVirtualFile;
import jetbrains.mps.nodefs.NodeVirtualFileSystem;
import jetbrains.mps.project.MPSProject;
import jetbrains.mps.project.Project;
import java.awt.Component;
import org.jetbrains.mps.util.Condition;
import jetbrains.mps.openapi.intentions.IntentionExecutable;
import jetbrains.mps.openapi.editor.EditorContext;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import jetbrains.mps.workbench.action.ActionUtils;
import com.intellij.openapi.actionSystem.ActionPlaces;
import javax.swing.SwingUtilities;
import jetbrains.mps.smodel.ModelAccess;
import jetbrains.mps.openapi.editor.cells.EditorCell;
import jetbrains.mps.openapi.editor.cells.EditorCell_Collection;
import jetbrains.mps.nodeEditor.cells.CellFinderUtil;
import jetbrains.mps.nodeEditor.inspector.InspectorEditorComponent;
import com.intellij.openapi.command.impl.CurrentEditorProvider;
import com.intellij.openapi.fileEditor.FileEditor;
import org.apache.log4j.Level;
/**
* Common ancestor for all generated EditorTestCase instances
* TODO must be moved up to the platform level: the MPSNodeEditor is availalbe only in ide
*/
public abstract class BaseEditorTestBody extends BaseTestBody {
private static final Logger LOG = LogManager.getLogger(BaseEditorTestBody.class);
private static DataManager DATA_MANAGER = new DataManagerImpl();
private EditorComponent myCurrentEditorComponent;
protected Editor myEditor;
private MPSFileNodeEditor myFileNodeEditor;
private SNode myBefore;
private SNode myResult;
protected CellReference myStart;
protected CellReference myFinish;
public abstract void testMethodImpl() throws Exception;
/**
*
* @deprecated use #initEditorComponent instead
*/
@Deprecated
protected Editor initEditor(final String before, final String after) {
if (LOG.isInfoEnabled()) {
LOG.info("Initializing editor");
}
final Throwable[] ts = new Throwable[1];
ThreadUtils.runInUIThreadAndWait(new Runnable() {
@Override
public void run() {
try {
BaseEditorTestBody.this.doInitEditor(before, after);
} catch (Throwable t) {
ts[0] = t;
}
}
});
try {
flushEDTEvents();
} catch (InvocationTargetException e) {
ts[0] = e;
} catch (InterruptedException e) {
ts[0] = e;
}
if (ts[0] != null) {
throw new RuntimeException("Exception while initializing the editor", ts[0]);
}
return myEditor;
}
protected EditorComponent initEditorComponent(String before, String after) {
initEditor(before, after);
return myCurrentEditorComponent;
}
private void doInitEditor(final String before, final String after) throws Exception {
addNodeById(before);
if (!(after.equals(""))) {
addNodeById(after);
}
myProject.getModelAccess().runWriteAction(new Runnable() {
public void run() {
myBefore = getNodeById(before);
myStart = findCellReference(getRealNodeById(before));
if (myStart == null) {
throw new IllegalStateException("Cannot find cell reference in the test case 'before'");
}
if (!(after.equals(""))) {
myResult = getNodeById(after);
myFinish = findCellReference(getRealNodeById(after));
}
myFileNodeEditor = openEditor();
myEditor = myFileNodeEditor.getNodeEditor();
myCurrentEditorComponent = myEditor.getCurrentEditorComponent();
if (!(myCurrentEditorComponent instanceof NodeEditorComponent)) {
throw new IllegalArgumentException("The component is not an instance of NodeEditorComponent: " + myCurrentEditorComponent);
}
NodeEditorComponent component = (NodeEditorComponent) myCurrentEditorComponent;
component.addNotify();
component.setSize(component.getPreferredSize());
component.validate();
myCurrentEditorComponent = myStart.setupSelection(component);
}
});
}
private CellReference findCellReference(SNode node) {
List<SNode> annotations = SNodeOperations.getNodeDescendants(node, MetaAdapterFactory.getConcept(0x8585453e6bfb4d80L, 0x98deb16074f1d86cL, 0x11e31babe12L, "jetbrains.mps.lang.test.structure.AnonymousCellAnnotation"), false, new SAbstractConcept[]{});
if (ListSequence.fromList(annotations).isEmpty()) {
return null;
}
return new CellReference(getNodeById(SNodeOperations.getParent(ListSequence.fromList(annotations).first()).getNodeId().toString()), ListSequence.fromList(annotations).first(), myMap);
}
protected void checkAssertion() throws Throwable {
final Wrappers._T<Throwable> throwable = new Wrappers._T<Throwable>(null);
flushEvents();
// FIXME why do we need model write here?
ThreadUtils.runInUIThreadAndWait(new Runnable() {
public void run() {
myProject.getModelAccess().runWriteAction(new Runnable() {
public void run() {
if (myResult != null) {
try {
SNode editedNode = myBefore;
NodesMatcher nm = new NodesMatcher();
List<NodeDifference> diff = nm.match(Collections.singletonList(editedNode), Collections.singletonList(myResult));
Assert.assertEquals(null, diff);
if (myFinish != null) {
myFinish.assertSelectionIsTheSame(myCurrentEditorComponent, (Map<SNode, SNode>) nm.getMap());
}
} catch (Throwable t) {
throwable.value = t;
}
}
}
});
}
});
flushEvents();
if (throwable.value != null) {
throw throwable.value;
}
}
public void testMethod() throws Throwable {
CachingAppender appender = installAppender();
try {
testMethodImpl();
checkAssertion();
dispose();
appender.sealEvents();
if (appender.isNotEmpty()) {
throw new UncleanTestExecutionException(appender);
}
} finally {
uninstallAppender(appender);
final Throwable[] ts = new Throwable[1];
myProject.getModelAccess().runWriteInEDT(new Runnable() {
public void run() {
try {
UndoManagerImpl undoManager = (UndoManagerImpl) UndoManager.getInstance(ProjectHelper.toIdeaProject(myProject));
MPSNodeVirtualFile file = NodeVirtualFileSystem.getInstance().getFileFor(myProject.getRepository(), BaseEditorTestBody.this.myBefore);
undoManager.clearUndoRedoQueueInTests(file);
} catch (Throwable t) {
ts[0] = t;
}
}
});
if (ts[0] != null) {
throw new RuntimeException("Failure during editor test execution", ts[0]);
}
}
}
private void dispose() throws InterruptedException, InvocationTargetException {
final Throwable[] ts = new Throwable[1];
ThreadUtils.runInUIThreadAndWait(new Runnable() {
public void run() {
myProject.getModelAccess().runWriteAction(new Runnable() {
public void run() {
try {
myFileNodeEditor.dispose();
myFileNodeEditor = null;
} catch (Throwable t) {
ts[0] = t;
}
}
});
}
});
if (ts[0] != null) {
throw new RuntimeException("Failure during test disposal", ts[0]);
}
}
private MPSFileNodeEditor openEditor() {
assert ThreadUtils.isInEDT();
MPSNodeVirtualFile file = NodeVirtualFileSystem.getInstance().getFileFor(myProject.getRepository(), myBefore);
return new MPSFileNodeEditor((MPSProject) myProject, file);
}
protected jetbrains.mps.nodeEditor.EditorComponent getEditorComponent() {
return (jetbrains.mps.nodeEditor.EditorComponent) myCurrentEditorComponent;
}
/**
*
* @deprecated no need to refer the editor instance here -- EditorComponent is enough
*/
@Deprecated
protected Editor getEditor() {
return myEditor;
}
protected Project getProject() {
return myProject;
}
protected void typeString(final String text) throws InterruptedException, InvocationTargetException {
new KeyEventsDispatcher(this).typeString(text);
}
protected void pressKeys(final List<String> keyStrokes) throws InterruptedException, InvocationTargetException {
new KeyEventsDispatcher(this).pressKeys(keyStrokes);
}
protected Component processMouseEvent(int x, int y, int eventType) throws InvocationTargetException, InterruptedException {
return new MouseEventsDispatcher(this).processMouseEvent(x, y, eventType);
}
protected void processSecondaryMouseEvent(final Component targetComponent, int x, int y, int eventType) throws InvocationTargetException, InterruptedException {
new MouseEventsDispatcher(this).processSecondaryMouseEvent(targetComponent, x, y, eventType);
}
protected void invokeIntention(String id, SNode node) throws InterruptedException, InvocationTargetException {
invokeMatchingIntention(node, new MatchIntentionById(id));
}
protected void invokeParameterizedIntention(String id, Object parameter, SNode node) throws InterruptedException, InvocationTargetException {
invokeMatchingIntention(node, new MatchIntentionByIdAndParameter(id, parameter));
}
protected void invokeMatchingIntention(final SNode node, Condition<IntentionExecutable> intentionCondition) throws InterruptedException, InvocationTargetException {
new IntentionTester(this).invokeMatchingIntention(node, intentionCondition);
}
protected boolean isIntentionApplicable(String intentionId, SNode node) throws InterruptedException, InvocationTargetException {
return new IntentionTester(this).isIntentionApplicable(intentionId, node);
}
public EditorContext getEditorContext() {
return myCurrentEditorComponent.getEditorContext();
}
protected void invokeAction(final String actionId) throws InvocationTargetException, InterruptedException {
final AnAction action = ActionManager.getInstance().getAction(actionId);
final AnActionEvent event = ActionUtils.createEvent(ActionPlaces.MAIN_MENU, DATA_MANAGER.getDataContext(getEditorComponent()));
runUndoableInEDTAndWait(new Runnable() {
public void run() {
action.actionPerformed(event);
}
});
}
private void flushEDTEvents() throws InvocationTargetException, InterruptedException {
// wait for all events currently in EDT queue
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
// empty task
}
});
// flushing model events
flushEvents();
}
private void flushEvents() {
ModelAccess.instance().flushEventQueue();
}
protected void switchToInspector() throws InvocationTargetException, InterruptedException {
if (!(myCurrentEditorComponent instanceof NodeEditorComponent)) {
throw new IllegalArgumentException("Impossible to switch to inspector: the component is not a NodeEditorComponent: " + myCurrentEditorComponent);
}
myCurrentEditorComponent = ((NodeEditorComponent) myCurrentEditorComponent).getInspector();
if (myCurrentEditorComponent.getSelectionManager().getSelection() == null) {
selectFirstCellInInspector(myCurrentEditorComponent);
}
}
private void selectFirstCellInInspector(EditorComponent myCurrentEditorComponent) {
EditorCell toSelect = null;
EditorCell rootCell = myCurrentEditorComponent.getRootCell();
if (rootCell instanceof EditorCell_Collection) {
toSelect = CellFinderUtil.findChildByManyFinders(rootCell, CellFinderUtil.Finder.FIRST_EDITABLE, CellFinderUtil.Finder.FIRST_SELECTABLE_LEAF);
if (toSelect == null) {
toSelect = rootCell;
}
} else
if (rootCell != null && rootCell.isSelectable()) {
toSelect = rootCell;
}
if (toSelect != null) {
myCurrentEditorComponent.changeSelection(toSelect);
}
}
protected void switchBackFromInspector() {
if (!(myCurrentEditorComponent instanceof InspectorEditorComponent)) {
throw new IllegalArgumentException("Impossible to switch back from inspector: the component is not a InspectorEditorComponent: " + myCurrentEditorComponent);
}
myCurrentEditorComponent = myFileNodeEditor.getNodeEditor().getCurrentEditorComponent();
}
public void runUndoableCommandInEDTAndWait(final Runnable runnable) throws InterruptedException, InvocationTargetException {
runUndoableInEDTAndWait(new Runnable() {
public void run() {
getProject().getModelAccess().executeCommand(runnable);
}
});
}
public void runUndoableInEDTAndWait(final Runnable runnable) throws InvocationTargetException, InterruptedException {
UndoManagerImpl undoManager = (UndoManagerImpl) UndoManager.getInstance(ProjectHelper.toIdeaProject(myProject));
CurrentEditorProvider oldEditorProvider = undoManager.getEditorProvider();
undoManager.setEditorProvider(new CurrentEditorProvider() {
public FileEditor getCurrentEditor() {
return myFileNodeEditor;
}
});
Exception exception = ThreadUtils.runInUIThreadAndWait(runnable);
if (exception != null) {
throw new RuntimeException("Failure during editor test execution", exception);
}
flushEDTEvents();
// some actions (Copy/Paste) are running one more command later
flushEDTEvents();
undoManager.setEditorProvider(oldEditorProvider);
}
private CachingAppender installAppender() {
Logger rootLogger = Logger.getRootLogger();
CachingAppender appender = new CachingAppender(Level.ERROR);
populateExpectedEvents(appender);
rootLogger.addAppender(appender);
return appender;
}
protected void populateExpectedEvents(CachingAppender appender) {
}
private void uninstallAppender(CachingAppender appender) {
Logger.getRootLogger().removeAppender(appender);
}
}