package com.sap.ide.refactoring.core.textual; import static com.sap.ide.refactoring.test.RefactoringAssertionUtil.assertEqualsIgnoreWhitespaces; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Collection; import ngpm.NgpmPackage; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.junit.Ignore; import org.junit.Test; import textblocks.TextBlock; import com.sap.ide.refactoring.core.AbstractRefactoringCommand; import com.sap.ide.refactoring.core.RefactoringCoreException; import com.sap.ide.refactoring.core.execution.RefactoringCommandExecutor; import com.sap.ide.refactoring.core.execution.RefactoringResult; import com.sap.ide.refactoring.test.RefactoringBaseTest; import com.sap.mi.textual.parsing.textblocks.TbDebugUtil; import com.sap.tc.moin.repository.Connection; import com.sap.tc.moin.repository.commands.PartitionOperation; import com.sap.tc.moin.repository.mmi.reflect.RefObject; import data.classes.MethodSignature; import data.classes.SapClass; import data.classes.SignatureImplementation; /** * Tests the integration of {@link RefactoringCommandExecutor}, * {@link TextBlockInChangeCalculator} and {@link TextBlocksSynchronizationCommand}. * * @author D049157 * */ public class TestCommandExecutionPrettyPrintInteraction extends RefactoringBaseTest { private static class NothingChangedCommand extends AbstractRefactoringCommand { private final SapClass clazz; protected NothingChangedCommand(Connection connection, SapClass clazz) { super(connection, "Do nothing"); this.clazz = clazz; } @Override protected void performRefactoring(IProgressMonitor pm) { for (MethodSignature method : new ArrayList<MethodSignature>(clazz.getOwnedSignatures())) { // Do something to trigger events. Then make everything the way before clazz.getOwnedSignatures().remove(method); clazz.getOwnedSignatures().add(method); } } @Override public Collection<PartitionOperation> getAffectedPartitions() { return null; } } private class CreateMethodCommand extends AbstractRefactoringCommand { private final SapClass clazz; private final String methodName; protected CreateMethodCommand(Connection connection, SapClass clazz, String methodName) { super(connection, "Creat method " + methodName); this.clazz = clazz; this.methodName = methodName; } @Override protected void performRefactoring(IProgressMonitor pm) { NgpmPackage rootPkg = connection.getPackage(NgpmPackage.PACKAGE_DESCRIPTOR); MethodSignature method = (MethodSignature) rootPkg.getData().getClasses().getMethodSignature().refCreateInstance(); method.setName(methodName); SignatureImplementation impl = (SignatureImplementation) rootPkg.getBehavioral().getActions().getBlock() .refCreateInstance(); impl.setImplements(method); clazz.getOwnedSignatures().add(method); } @Override public Collection<PartitionOperation> getAffectedPartitions() { return null; } } private static class DeleteMethodCommand extends AbstractRefactoringCommand { private final SapClass clazz; private final String methodName; protected DeleteMethodCommand(Connection connection, SapClass clazz, String methodName) { super(connection, "Delete method " + methodName); this.clazz = clazz; this.methodName = methodName; } @Override protected void performRefactoring(IProgressMonitor pm) { for (MethodSignature method : clazz.getOwnedSignatures()) { if (method.getName().equals(methodName)) { method.refDelete(); } } } @Override public Collection<PartitionOperation> getAffectedPartitions() { return null; } } private static class MoveMethodCommand extends AbstractRefactoringCommand { private final String methodName; private final SapClass sourceClazz; private final SapClass targetClazz; protected MoveMethodCommand(Connection connection, SapClass sourceClazz, SapClass targetClazz, String methodName) { super(connection, "Move method " + methodName); this.sourceClazz = sourceClazz; assert sourceClazz != null; this.targetClazz = targetClazz; assert targetClazz != null; this.methodName = methodName; } @Override protected void performRefactoring(IProgressMonitor pm) { for (MethodSignature method : new ArrayList<MethodSignature>(sourceClazz.getOwnedSignatures())) { if (method.getName().equals(methodName)) { sourceClazz.getOwnedSignatures().remove(method); targetClazz.getOwnedSignatures().add(method); } } } @Override public Collection<PartitionOperation> getAffectedPartitions() { return null; } } /** * System under test */ private RefactoringCommandExecutor cmdExecutor; private RefactoringEditorFacade facade; @Test public void testSomethingChangedButNothingNeedsPrettyPrinting() throws RefactoringCoreException { facade = createEditorFacadeForRunletClass("ClassWithEmptyMethod"); SapClass clazz = (SapClass) facade.getDecoratedDomainRootObject(); AbstractRefactoringCommand cmd = new NothingChangedCommand(facade.getEditorConnection(), clazz); cmdExecutor = new RefactoringCommandExecutorTestable(facade, cmd); RefactoringResult result = cmdExecutor.runRefactoring(new NullProgressMonitor()); TextBlockChange textualChange = getTextBlockChange(result.change, clazz); String currentContent = textualChange.getCurrentContent(new NullProgressMonitor()); String previewContent = textualChange.getPreviewContent(new NullProgressMonitor()); assertEqualsIgnoreWhitespaces("Content must remain unchanged", currentContent, previewContent); assertEquals("Content and formatting must remain unchanged", currentContent, previewContent); } @Test public void testCreateModelElementNeedsToTriggerTextBlocksCreation() throws RefactoringCoreException { facade = createEditorFacadeForRunletClass("Class2"); SapClass clazz = (SapClass) facade.getDecoratedDomainRootObject(); AbstractRefactoringCommand cmd = new CreateMethodCommand(facade.getEditorConnection(), clazz, "NewMethod"); cmdExecutor = new RefactoringCommandExecutorTestable(facade, cmd); RefactoringResult result = cmdExecutor.runRefactoring(new NullProgressMonitor()); TextBlockChange textualChange = getTextBlockChange(result.change, clazz); String currentContent = textualChange.getCurrentContent(new NullProgressMonitor()); String previewContent = textualChange.getPreviewContent(new NullProgressMonitor()); assertFalse("Method must not have been existed before", currentContent.contains("void NewMethod()")); assertTrue("New method must have been created", previewContent.contains("void NewMethod()")); } @Test public void testDeleteModelElementNeedsToTriggerTextBlocksDeletion() throws RefactoringCoreException { facade = createEditorFacadeForRunletClass("ClassWithEmptyMethod"); SapClass clazz = (SapClass) facade.getDecoratedDomainRootObject(); AbstractRefactoringCommand cmd = new DeleteMethodCommand(facade.getEditorConnection(), clazz, "m"); cmdExecutor = new RefactoringCommandExecutorTestable(facade, cmd); RefactoringResult result = cmdExecutor.runRefactoring(new NullProgressMonitor()); TextBlockChange textualChange = getTextBlockChange(result.change, clazz); String currentContent = textualChange.getCurrentContent(new NullProgressMonitor()); String previewContent = textualChange.getPreviewContent(new NullProgressMonitor()); assertTrue("Method must exist before", currentContent.contains("void m()")); assertFalse("Method must not exist any longer", previewContent.contains("void m()")); } @Test public void testMovedModelElementTakesTextWithIt() throws CoreException, RefactoringCoreException { facade = createEditorFacadeForRunletClass("RedefineParameterTst2"); SapClass sourceClazz = findRunletClass("RedefineParameterTst2"); SapClass targetClazz = findRunletClass("Class2"); AbstractRefactoringCommand cmd = new MoveMethodCommand(facade.getEditorConnection(), sourceClazz, targetClazz, "n"); cmdExecutor = new RefactoringCommandExecutorTestable(facade, cmd); // Assert that the model is as we expect it to be assertTrue("n".equals(sourceClazz.getOwnedSignatures().iterator().next().getName())); assertTrue(targetClazz.getOwnedSignatures().isEmpty()); RefactoringResult result = cmdExecutor.runRefactoring(new NullProgressMonitor()); TextBlockChange changeOfClass2 = getTextBlockChange(result.change, targetClazz); TextBlockChange changeOfThisToParameterChange = getTextBlockChange(result.change, sourceClazz); String currentContentOfThisFoo = changeOfThisToParameterChange.getCurrentContent(new NullProgressMonitor()); String previewContentOfThisFoo = changeOfThisToParameterChange.getPreviewContent(new NullProgressMonitor()); String currentContentOfClass2 = changeOfClass2.getCurrentContent(new NullProgressMonitor()); String previewContentOfClass2 = changeOfClass2.getPreviewContent(new NullProgressMonitor()); // Assert that the model is as we expect it to be result.change.perform(new NullProgressMonitor()); // re-apply so that we can check the model assertTrue("n".equals(targetClazz.getOwnedSignatures().iterator().next().getName())); assertTrue(sourceClazz.getOwnedSignatures().isEmpty()); // Check that the text has been changed as expected. assertTrue("Method must exist before in " + currentContentOfThisFoo, currentContentOfThisFoo.contains("n(")); assertFalse("Method must not exist any longer in " + previewContentOfThisFoo, previewContentOfThisFoo.contains("n(")); assertFalse("Method must not exist before", currentContentOfClass2.contains("n(")); assertTrue("Method must exist afterwards in " + previewContentOfClass2, previewContentOfClass2.contains("n(")); } /** * The following is a whitebox test. It makes some heavy assumptions and might * break rather often... * * @throws CoreException * @throws RefactoringCoreException */ @Test @Ignore // will work ater reparsing of the TB model public void testMovedModelElementEvenTakesTextBlockWithIt() throws CoreException, RefactoringCoreException { facade = createEditorFacadeForRunletClass("RedefineParameterTst2"); SapClass sourceClazz = findRunletClass("RedefineParameterTst2"); SapClass targetClazz = findRunletClass("Class2"); AbstractRefactoringCommand cmd = new MoveMethodCommand(facade.getEditorConnection(), sourceClazz, targetClazz, "n"); cmdExecutor = new RefactoringCommandExecutorTestable(facade, cmd); RefactoringResult result = cmdExecutor.runRefactoring(new NullProgressMonitor()); TextBlock sourceRootBlock = (TextBlock) TextBlockRefactoringUtil.findCorrespondingTextBlocks(sourceClazz, "Class").iterator().next(); TextBlock methodTextBlockBefore = sourceRootBlock.getSubBlocks().iterator().next(); String methodAsStringBefore = TbDebugUtil.getDocumentNodeAsPlainString(methodTextBlockBefore); result.change.perform(new NullProgressMonitor()); TextBlock targetRootBlock = (TextBlock) TextBlockRefactoringUtil.findCorrespondingTextBlocks(targetClazz, "Class").iterator().next(); TextBlock methodTextBlockAfter = targetRootBlock.getSubBlocks().iterator().next(); String methodAsStringAfter = TbDebugUtil.getDocumentNodeAsPlainString(methodTextBlockAfter); assertEquals("Method TB must have moved!", methodAsStringBefore, methodAsStringAfter); } /** * Makes heavy assumptions on how we compose our changes. Likely to break... */ private TextBlockChange getTextBlockChange(Change compositeChange, RefObject changedRefObject) { try { RefactoringStatus status = compositeChange.isValid(new NullProgressMonitor()); assertTrue("Change must be valid. Otherwise, this implies that the change operation failed: " + status.toString(), status.isOK()); } catch (Exception e) { fail(e.getMessage()); } Change[] changes = ((CompositeChange) ((CompositeChange) compositeChange).getChildren()[1]).getChildren(); for (Change change : changes) { TextBlockChange tbChange = (TextBlockChange) change; if (changedRefObject.equals(tbChange.getModifiedElement())) { return tbChange; } } throw new AssertionError("Unable to find change for " + changedRefObject); } }